1c822490fSShubhrajyoti Datta // SPDX-License-Identifier: GPL-2.0
2c822490fSShubhrajyoti Datta /*
3c822490fSShubhrajyoti Datta  * Xilinx 'Clocking Wizard' driver
4c822490fSShubhrajyoti Datta  *
5c822490fSShubhrajyoti Datta  *  Copyright (C) 2013 - 2021 Xilinx
6c822490fSShubhrajyoti Datta  *
7c822490fSShubhrajyoti Datta  *  Sören Brinkmann <soren.brinkmann@xilinx.com>
8c822490fSShubhrajyoti Datta  *
9c822490fSShubhrajyoti Datta  */
10c822490fSShubhrajyoti Datta 
11595c88cdSShubhrajyoti Datta #include <linux/bitfield.h>
12c822490fSShubhrajyoti Datta #include <linux/platform_device.h>
13c822490fSShubhrajyoti Datta #include <linux/clk.h>
14c822490fSShubhrajyoti Datta #include <linux/clk-provider.h>
15c822490fSShubhrajyoti Datta #include <linux/slab.h>
16c822490fSShubhrajyoti Datta #include <linux/io.h>
17c822490fSShubhrajyoti Datta #include <linux/of.h>
18595c88cdSShubhrajyoti Datta #include <linux/math64.h>
19c822490fSShubhrajyoti Datta #include <linux/module.h>
20c822490fSShubhrajyoti Datta #include <linux/err.h>
21c822490fSShubhrajyoti Datta #include <linux/iopoll.h>
22c822490fSShubhrajyoti Datta 
23c822490fSShubhrajyoti Datta #define WZRD_NUM_OUTPUTS	7
24c822490fSShubhrajyoti Datta #define WZRD_ACLK_MAX_FREQ	250000000UL
25c822490fSShubhrajyoti Datta 
26c822490fSShubhrajyoti Datta #define WZRD_CLK_CFG_REG(n)	(0x200 + 4 * (n))
27c822490fSShubhrajyoti Datta 
28c822490fSShubhrajyoti Datta #define WZRD_CLKOUT0_FRAC_EN	BIT(18)
29c822490fSShubhrajyoti Datta #define WZRD_CLKFBOUT_FRAC_EN	BIT(26)
30c822490fSShubhrajyoti Datta 
31c822490fSShubhrajyoti Datta #define WZRD_CLKFBOUT_MULT_SHIFT	8
32c822490fSShubhrajyoti Datta #define WZRD_CLKFBOUT_MULT_MASK		(0xff << WZRD_CLKFBOUT_MULT_SHIFT)
33c822490fSShubhrajyoti Datta #define WZRD_CLKFBOUT_FRAC_SHIFT	16
34c822490fSShubhrajyoti Datta #define WZRD_CLKFBOUT_FRAC_MASK		(0x3ff << WZRD_CLKFBOUT_FRAC_SHIFT)
35c822490fSShubhrajyoti Datta #define WZRD_DIVCLK_DIVIDE_SHIFT	0
36c822490fSShubhrajyoti Datta #define WZRD_DIVCLK_DIVIDE_MASK		(0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
37c822490fSShubhrajyoti Datta #define WZRD_CLKOUT_DIVIDE_SHIFT	0
38c822490fSShubhrajyoti Datta #define WZRD_CLKOUT_DIVIDE_WIDTH	8
39c822490fSShubhrajyoti Datta #define WZRD_CLKOUT_DIVIDE_MASK		(0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
40c822490fSShubhrajyoti Datta #define WZRD_CLKOUT_FRAC_SHIFT		8
41c822490fSShubhrajyoti Datta #define WZRD_CLKOUT_FRAC_MASK		0x3ff
42595c88cdSShubhrajyoti Datta #define WZRD_CLKOUT0_FRAC_MASK		GENMASK(17, 8)
43c822490fSShubhrajyoti Datta 
44c822490fSShubhrajyoti Datta #define WZRD_DR_MAX_INT_DIV_VALUE	255
45c822490fSShubhrajyoti Datta #define WZRD_DR_STATUS_REG_OFFSET	0x04
46c822490fSShubhrajyoti Datta #define WZRD_DR_LOCK_BIT_MASK		0x00000001
47c822490fSShubhrajyoti Datta #define WZRD_DR_INIT_REG_OFFSET		0x25C
48c822490fSShubhrajyoti Datta #define WZRD_DR_DIV_TO_PHASE_OFFSET	4
49c822490fSShubhrajyoti Datta #define WZRD_DR_BEGIN_DYNA_RECONF	0x03
50dd5e7431SShubhrajyoti Datta #define WZRD_DR_BEGIN_DYNA_RECONF_5_2	0x07
51dd5e7431SShubhrajyoti Datta #define WZRD_DR_BEGIN_DYNA_RECONF1_5_2	0x02
52c822490fSShubhrajyoti Datta 
53c822490fSShubhrajyoti Datta #define WZRD_USEC_POLL		10
54c822490fSShubhrajyoti Datta #define WZRD_TIMEOUT_POLL		1000
55595c88cdSShubhrajyoti Datta 
56595c88cdSShubhrajyoti Datta /* Divider limits, from UG572 Table 3-4 for Ultrascale+ */
57595c88cdSShubhrajyoti Datta #define DIV_O				0x01
58595c88cdSShubhrajyoti Datta #define DIV_ALL				0x03
59595c88cdSShubhrajyoti Datta 
60595c88cdSShubhrajyoti Datta #define WZRD_M_MIN			2
61595c88cdSShubhrajyoti Datta #define WZRD_M_MAX			128
62595c88cdSShubhrajyoti Datta #define WZRD_D_MIN			1
63595c88cdSShubhrajyoti Datta #define WZRD_D_MAX			106
64595c88cdSShubhrajyoti Datta #define WZRD_VCO_MIN			800000000
65595c88cdSShubhrajyoti Datta #define WZRD_VCO_MAX			1600000000
66595c88cdSShubhrajyoti Datta #define WZRD_O_MIN			1
67595c88cdSShubhrajyoti Datta #define WZRD_O_MAX			128
68595c88cdSShubhrajyoti Datta #define WZRD_MIN_ERR			20000
69595c88cdSShubhrajyoti Datta #define WZRD_FRAC_POINTS		1000
70595c88cdSShubhrajyoti Datta 
71c822490fSShubhrajyoti Datta /* Get the mask from width */
72c822490fSShubhrajyoti Datta #define div_mask(width)			((1 << (width)) - 1)
73c822490fSShubhrajyoti Datta 
74c822490fSShubhrajyoti Datta /* Extract divider instance from clock hardware instance */
75c822490fSShubhrajyoti Datta #define to_clk_wzrd_divider(_hw) container_of(_hw, struct clk_wzrd_divider, hw)
76c822490fSShubhrajyoti Datta 
77c822490fSShubhrajyoti Datta enum clk_wzrd_int_clks {
78c822490fSShubhrajyoti Datta 	wzrd_clk_mul,
79c822490fSShubhrajyoti Datta 	wzrd_clk_mul_div,
80c822490fSShubhrajyoti Datta 	wzrd_clk_mul_frac,
81c822490fSShubhrajyoti Datta 	wzrd_clk_int_max
82c822490fSShubhrajyoti Datta };
83c822490fSShubhrajyoti Datta 
84c822490fSShubhrajyoti Datta /**
85c822490fSShubhrajyoti Datta  * struct clk_wzrd - Clock wizard private data structure
86c822490fSShubhrajyoti Datta  *
87c822490fSShubhrajyoti Datta  * @clk_data:		Clock data
88c822490fSShubhrajyoti Datta  * @nb:			Notifier block
89c822490fSShubhrajyoti Datta  * @base:		Memory base
90c822490fSShubhrajyoti Datta  * @clk_in1:		Handle to input clock 'clk_in1'
91c822490fSShubhrajyoti Datta  * @axi_clk:		Handle to input clock 's_axi_aclk'
92c822490fSShubhrajyoti Datta  * @clks_internal:	Internal clocks
93c822490fSShubhrajyoti Datta  * @clkout:		Output clocks
94c822490fSShubhrajyoti Datta  * @speed_grade:	Speed grade of the device
95c822490fSShubhrajyoti Datta  * @suspended:		Flag indicating power state of the device
96c822490fSShubhrajyoti Datta  */
97c822490fSShubhrajyoti Datta struct clk_wzrd {
98c822490fSShubhrajyoti Datta 	struct clk_onecell_data clk_data;
99c822490fSShubhrajyoti Datta 	struct notifier_block nb;
100c822490fSShubhrajyoti Datta 	void __iomem *base;
101c822490fSShubhrajyoti Datta 	struct clk *clk_in1;
102c822490fSShubhrajyoti Datta 	struct clk *axi_clk;
103c822490fSShubhrajyoti Datta 	struct clk *clks_internal[wzrd_clk_int_max];
104c822490fSShubhrajyoti Datta 	struct clk *clkout[WZRD_NUM_OUTPUTS];
105c822490fSShubhrajyoti Datta 	unsigned int speed_grade;
106c822490fSShubhrajyoti Datta 	bool suspended;
107c822490fSShubhrajyoti Datta };
108c822490fSShubhrajyoti Datta 
109c822490fSShubhrajyoti Datta /**
110c822490fSShubhrajyoti Datta  * struct clk_wzrd_divider - clock divider specific to clk_wzrd
111c822490fSShubhrajyoti Datta  *
112c822490fSShubhrajyoti Datta  * @hw:		handle between common and hardware-specific interfaces
113c822490fSShubhrajyoti Datta  * @base:	base address of register containing the divider
114c822490fSShubhrajyoti Datta  * @offset:	offset address of register containing the divider
115c822490fSShubhrajyoti Datta  * @shift:	shift to the divider bit field
116c822490fSShubhrajyoti Datta  * @width:	width of the divider bit field
117c822490fSShubhrajyoti Datta  * @flags:	clk_wzrd divider flags
118c822490fSShubhrajyoti Datta  * @table:	array of value/divider pairs, last entry should have div = 0
119595c88cdSShubhrajyoti Datta  * @m:	value of the multiplier
120595c88cdSShubhrajyoti Datta  * @d:	value of the common divider
121595c88cdSShubhrajyoti Datta  * @o:	value of the leaf divider
122c822490fSShubhrajyoti Datta  * @lock:	register lock
123c822490fSShubhrajyoti Datta  */
124c822490fSShubhrajyoti Datta struct clk_wzrd_divider {
125c822490fSShubhrajyoti Datta 	struct clk_hw hw;
126c822490fSShubhrajyoti Datta 	void __iomem *base;
127c822490fSShubhrajyoti Datta 	u16 offset;
128c822490fSShubhrajyoti Datta 	u8 shift;
129c822490fSShubhrajyoti Datta 	u8 width;
130c822490fSShubhrajyoti Datta 	u8 flags;
131c822490fSShubhrajyoti Datta 	const struct clk_div_table *table;
132595c88cdSShubhrajyoti Datta 	u32 m;
133595c88cdSShubhrajyoti Datta 	u32 d;
134595c88cdSShubhrajyoti Datta 	u32 o;
135c822490fSShubhrajyoti Datta 	spinlock_t *lock;  /* divider lock */
136c822490fSShubhrajyoti Datta };
137c822490fSShubhrajyoti Datta 
138c822490fSShubhrajyoti Datta #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
139c822490fSShubhrajyoti Datta 
140c822490fSShubhrajyoti Datta /* maximum frequencies for input/output clocks per speed grade */
141c822490fSShubhrajyoti Datta static const unsigned long clk_wzrd_max_freq[] = {
142c822490fSShubhrajyoti Datta 	800000000UL,
143c822490fSShubhrajyoti Datta 	933000000UL,
144c822490fSShubhrajyoti Datta 	1066000000UL
145c822490fSShubhrajyoti Datta };
146c822490fSShubhrajyoti Datta 
147c822490fSShubhrajyoti Datta /* spin lock variable for clk_wzrd */
148c822490fSShubhrajyoti Datta static DEFINE_SPINLOCK(clkwzrd_lock);
149c822490fSShubhrajyoti Datta 
clk_wzrd_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)150c822490fSShubhrajyoti Datta static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw,
151c822490fSShubhrajyoti Datta 					  unsigned long parent_rate)
152c822490fSShubhrajyoti Datta {
153c822490fSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
154c822490fSShubhrajyoti Datta 	void __iomem *div_addr = divider->base + divider->offset;
155c822490fSShubhrajyoti Datta 	unsigned int val;
156c822490fSShubhrajyoti Datta 
157c822490fSShubhrajyoti Datta 	val = readl(div_addr) >> divider->shift;
158c822490fSShubhrajyoti Datta 	val &= div_mask(divider->width);
159c822490fSShubhrajyoti Datta 
160c822490fSShubhrajyoti Datta 	return divider_recalc_rate(hw, parent_rate, val, divider->table,
161c822490fSShubhrajyoti Datta 			divider->flags, divider->width);
162c822490fSShubhrajyoti Datta }
163c822490fSShubhrajyoti Datta 
clk_wzrd_dynamic_reconfig(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)164c822490fSShubhrajyoti Datta static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate,
165c822490fSShubhrajyoti Datta 				     unsigned long parent_rate)
166c822490fSShubhrajyoti Datta {
167c822490fSShubhrajyoti Datta 	int err;
168c822490fSShubhrajyoti Datta 	u32 value;
169c822490fSShubhrajyoti Datta 	unsigned long flags = 0;
170c822490fSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
171c822490fSShubhrajyoti Datta 	void __iomem *div_addr = divider->base + divider->offset;
172c822490fSShubhrajyoti Datta 
173c822490fSShubhrajyoti Datta 	if (divider->lock)
174c822490fSShubhrajyoti Datta 		spin_lock_irqsave(divider->lock, flags);
175c822490fSShubhrajyoti Datta 	else
176c822490fSShubhrajyoti Datta 		__acquire(divider->lock);
177c822490fSShubhrajyoti Datta 
178c822490fSShubhrajyoti Datta 	value = DIV_ROUND_CLOSEST(parent_rate, rate);
179c822490fSShubhrajyoti Datta 
180c822490fSShubhrajyoti Datta 	/* Cap the value to max */
181c822490fSShubhrajyoti Datta 	min_t(u32, value, WZRD_DR_MAX_INT_DIV_VALUE);
182c822490fSShubhrajyoti Datta 
183c822490fSShubhrajyoti Datta 	/* Set divisor and clear phase offset */
184c822490fSShubhrajyoti Datta 	writel(value, div_addr);
185c822490fSShubhrajyoti Datta 	writel(0x00, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET);
186c822490fSShubhrajyoti Datta 
187c822490fSShubhrajyoti Datta 	/* Check status register */
188c822490fSShubhrajyoti Datta 	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET,
189c822490fSShubhrajyoti Datta 				 value, value & WZRD_DR_LOCK_BIT_MASK,
190c822490fSShubhrajyoti Datta 				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
191c822490fSShubhrajyoti Datta 	if (err)
192c822490fSShubhrajyoti Datta 		goto err_reconfig;
193c822490fSShubhrajyoti Datta 
194c822490fSShubhrajyoti Datta 	/* Initiate reconfiguration */
195dd5e7431SShubhrajyoti Datta 	writel(WZRD_DR_BEGIN_DYNA_RECONF_5_2,
196dd5e7431SShubhrajyoti Datta 	       divider->base + WZRD_DR_INIT_REG_OFFSET);
197dd5e7431SShubhrajyoti Datta 	writel(WZRD_DR_BEGIN_DYNA_RECONF1_5_2,
198c822490fSShubhrajyoti Datta 	       divider->base + WZRD_DR_INIT_REG_OFFSET);
199c822490fSShubhrajyoti Datta 
200c822490fSShubhrajyoti Datta 	/* Check status register */
201c822490fSShubhrajyoti Datta 	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET,
202c822490fSShubhrajyoti Datta 				 value, value & WZRD_DR_LOCK_BIT_MASK,
203c822490fSShubhrajyoti Datta 				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
204c822490fSShubhrajyoti Datta err_reconfig:
205c822490fSShubhrajyoti Datta 	if (divider->lock)
206c822490fSShubhrajyoti Datta 		spin_unlock_irqrestore(divider->lock, flags);
207c822490fSShubhrajyoti Datta 	else
208c822490fSShubhrajyoti Datta 		__release(divider->lock);
209c822490fSShubhrajyoti Datta 	return err;
210c822490fSShubhrajyoti Datta }
211c822490fSShubhrajyoti Datta 
clk_wzrd_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)212c822490fSShubhrajyoti Datta static long clk_wzrd_round_rate(struct clk_hw *hw, unsigned long rate,
213c822490fSShubhrajyoti Datta 				unsigned long *prate)
214c822490fSShubhrajyoti Datta {
215c822490fSShubhrajyoti Datta 	u8 div;
216c822490fSShubhrajyoti Datta 
217c822490fSShubhrajyoti Datta 	/*
218c822490fSShubhrajyoti Datta 	 * since we don't change parent rate we just round rate to closest
219c822490fSShubhrajyoti Datta 	 * achievable
220c822490fSShubhrajyoti Datta 	 */
221c822490fSShubhrajyoti Datta 	div = DIV_ROUND_CLOSEST(*prate, rate);
222c822490fSShubhrajyoti Datta 
223c822490fSShubhrajyoti Datta 	return *prate / div;
224c822490fSShubhrajyoti Datta }
225c822490fSShubhrajyoti Datta 
clk_wzrd_get_divisors(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)226595c88cdSShubhrajyoti Datta static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate,
227595c88cdSShubhrajyoti Datta 				 unsigned long parent_rate)
228595c88cdSShubhrajyoti Datta {
229595c88cdSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
230595c88cdSShubhrajyoti Datta 	unsigned long vco_freq, freq, diff;
231595c88cdSShubhrajyoti Datta 	u32 m, d, o;
232595c88cdSShubhrajyoti Datta 
233595c88cdSShubhrajyoti Datta 	for (m = WZRD_M_MIN; m <= WZRD_M_MAX; m++) {
234595c88cdSShubhrajyoti Datta 		for (d = WZRD_D_MIN; d <= WZRD_D_MAX; d++) {
235595c88cdSShubhrajyoti Datta 			vco_freq = DIV_ROUND_CLOSEST((parent_rate * m), d);
236595c88cdSShubhrajyoti Datta 			if (vco_freq >= WZRD_VCO_MIN && vco_freq <= WZRD_VCO_MAX) {
237595c88cdSShubhrajyoti Datta 				for (o = WZRD_O_MIN; o <= WZRD_O_MAX; o++) {
238595c88cdSShubhrajyoti Datta 					freq = DIV_ROUND_CLOSEST_ULL(vco_freq, o);
239595c88cdSShubhrajyoti Datta 					diff = abs(freq - rate);
240595c88cdSShubhrajyoti Datta 
241595c88cdSShubhrajyoti Datta 					if (diff < WZRD_MIN_ERR) {
242595c88cdSShubhrajyoti Datta 						divider->m = m;
243595c88cdSShubhrajyoti Datta 						divider->d = d;
244595c88cdSShubhrajyoti Datta 						divider->o = o;
245595c88cdSShubhrajyoti Datta 						return 0;
246595c88cdSShubhrajyoti Datta 					}
247595c88cdSShubhrajyoti Datta 				}
248595c88cdSShubhrajyoti Datta 			}
249595c88cdSShubhrajyoti Datta 		}
250595c88cdSShubhrajyoti Datta 	}
251595c88cdSShubhrajyoti Datta 	return -EBUSY;
252595c88cdSShubhrajyoti Datta }
253595c88cdSShubhrajyoti Datta 
clk_wzrd_dynamic_all_nolock(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)254595c88cdSShubhrajyoti Datta static int clk_wzrd_dynamic_all_nolock(struct clk_hw *hw, unsigned long rate,
255595c88cdSShubhrajyoti Datta 				       unsigned long parent_rate)
256595c88cdSShubhrajyoti Datta {
257595c88cdSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
258595c88cdSShubhrajyoti Datta 	unsigned long vco_freq, rate_div, clockout0_div;
259595c88cdSShubhrajyoti Datta 	u32 reg, pre, value, f;
260595c88cdSShubhrajyoti Datta 	int err;
261595c88cdSShubhrajyoti Datta 
262595c88cdSShubhrajyoti Datta 	err = clk_wzrd_get_divisors(hw, rate, parent_rate);
263595c88cdSShubhrajyoti Datta 	if (err)
264595c88cdSShubhrajyoti Datta 		return err;
265595c88cdSShubhrajyoti Datta 
266595c88cdSShubhrajyoti Datta 	vco_freq = DIV_ROUND_CLOSEST(parent_rate * divider->m, divider->d);
267595c88cdSShubhrajyoti Datta 	rate_div = DIV_ROUND_CLOSEST_ULL((vco_freq * WZRD_FRAC_POINTS), rate);
268595c88cdSShubhrajyoti Datta 
269595c88cdSShubhrajyoti Datta 	clockout0_div = div_u64(rate_div,  WZRD_FRAC_POINTS);
270595c88cdSShubhrajyoti Datta 
271595c88cdSShubhrajyoti Datta 	pre = DIV_ROUND_CLOSEST_ULL(vco_freq * WZRD_FRAC_POINTS, rate);
272595c88cdSShubhrajyoti Datta 	f = (pre - (clockout0_div * WZRD_FRAC_POINTS));
273595c88cdSShubhrajyoti Datta 	f &= WZRD_CLKOUT_FRAC_MASK;
274595c88cdSShubhrajyoti Datta 
275595c88cdSShubhrajyoti Datta 	reg = FIELD_PREP(WZRD_CLKOUT_DIVIDE_MASK, clockout0_div) |
276595c88cdSShubhrajyoti Datta 	      FIELD_PREP(WZRD_CLKOUT0_FRAC_MASK, f);
277595c88cdSShubhrajyoti Datta 
278595c88cdSShubhrajyoti Datta 	writel(reg, divider->base + WZRD_CLK_CFG_REG(2));
279595c88cdSShubhrajyoti Datta 	/* Set divisor and clear phase offset */
280595c88cdSShubhrajyoti Datta 	reg = FIELD_PREP(WZRD_CLKFBOUT_MULT_MASK, divider->m) |
281595c88cdSShubhrajyoti Datta 	      FIELD_PREP(WZRD_DIVCLK_DIVIDE_MASK, divider->d);
282595c88cdSShubhrajyoti Datta 	writel(reg, divider->base + WZRD_CLK_CFG_REG(0));
283595c88cdSShubhrajyoti Datta 	writel(divider->o, divider->base + WZRD_CLK_CFG_REG(2));
284595c88cdSShubhrajyoti Datta 	writel(0, divider->base + WZRD_CLK_CFG_REG(3));
285595c88cdSShubhrajyoti Datta 	/* Check status register */
286595c88cdSShubhrajyoti Datta 	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
287595c88cdSShubhrajyoti Datta 				 value & WZRD_DR_LOCK_BIT_MASK,
288595c88cdSShubhrajyoti Datta 				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
289595c88cdSShubhrajyoti Datta 	if (err)
290595c88cdSShubhrajyoti Datta 		return -ETIMEDOUT;
291595c88cdSShubhrajyoti Datta 
292595c88cdSShubhrajyoti Datta 	/* Initiate reconfiguration */
293595c88cdSShubhrajyoti Datta 	writel(WZRD_DR_BEGIN_DYNA_RECONF,
294595c88cdSShubhrajyoti Datta 	       divider->base + WZRD_DR_INIT_REG_OFFSET);
295595c88cdSShubhrajyoti Datta 
296595c88cdSShubhrajyoti Datta 	/* Check status register */
297595c88cdSShubhrajyoti Datta 	return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
298595c88cdSShubhrajyoti Datta 				 value & WZRD_DR_LOCK_BIT_MASK,
299595c88cdSShubhrajyoti Datta 				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
300595c88cdSShubhrajyoti Datta }
301595c88cdSShubhrajyoti Datta 
clk_wzrd_dynamic_all(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)302595c88cdSShubhrajyoti Datta static int clk_wzrd_dynamic_all(struct clk_hw *hw, unsigned long rate,
303595c88cdSShubhrajyoti Datta 				unsigned long parent_rate)
304595c88cdSShubhrajyoti Datta {
305595c88cdSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
306595c88cdSShubhrajyoti Datta 	unsigned long flags = 0;
307595c88cdSShubhrajyoti Datta 	int ret;
308595c88cdSShubhrajyoti Datta 
309595c88cdSShubhrajyoti Datta 	spin_lock_irqsave(divider->lock, flags);
310595c88cdSShubhrajyoti Datta 
311595c88cdSShubhrajyoti Datta 	ret = clk_wzrd_dynamic_all_nolock(hw, rate, parent_rate);
312595c88cdSShubhrajyoti Datta 
313595c88cdSShubhrajyoti Datta 	spin_unlock_irqrestore(divider->lock, flags);
314595c88cdSShubhrajyoti Datta 
315595c88cdSShubhrajyoti Datta 	return ret;
316595c88cdSShubhrajyoti Datta }
317595c88cdSShubhrajyoti Datta 
clk_wzrd_recalc_rate_all(struct clk_hw * hw,unsigned long parent_rate)318595c88cdSShubhrajyoti Datta static unsigned long clk_wzrd_recalc_rate_all(struct clk_hw *hw,
319595c88cdSShubhrajyoti Datta 					      unsigned long parent_rate)
320595c88cdSShubhrajyoti Datta {
321595c88cdSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
322595c88cdSShubhrajyoti Datta 	u32 m, d, o, div, reg, f;
323595c88cdSShubhrajyoti Datta 
324595c88cdSShubhrajyoti Datta 	reg = readl(divider->base + WZRD_CLK_CFG_REG(0));
325595c88cdSShubhrajyoti Datta 	d = FIELD_GET(WZRD_DIVCLK_DIVIDE_MASK, reg);
326595c88cdSShubhrajyoti Datta 	m = FIELD_GET(WZRD_CLKFBOUT_MULT_MASK, reg);
327595c88cdSShubhrajyoti Datta 	reg = readl(divider->base + WZRD_CLK_CFG_REG(2));
328595c88cdSShubhrajyoti Datta 	o = FIELD_GET(WZRD_DIVCLK_DIVIDE_MASK, reg);
329595c88cdSShubhrajyoti Datta 	f = FIELD_GET(WZRD_CLKOUT0_FRAC_MASK, reg);
330595c88cdSShubhrajyoti Datta 
331595c88cdSShubhrajyoti Datta 	div = DIV_ROUND_CLOSEST(d * (WZRD_FRAC_POINTS * o + f), WZRD_FRAC_POINTS);
332595c88cdSShubhrajyoti Datta 	return divider_recalc_rate(hw, parent_rate * m, div, divider->table,
333595c88cdSShubhrajyoti Datta 			divider->flags, divider->width);
334595c88cdSShubhrajyoti Datta }
335595c88cdSShubhrajyoti Datta 
clk_wzrd_round_rate_all(struct clk_hw * hw,unsigned long rate,unsigned long * prate)336595c88cdSShubhrajyoti Datta static long clk_wzrd_round_rate_all(struct clk_hw *hw, unsigned long rate,
337595c88cdSShubhrajyoti Datta 				    unsigned long *prate)
338595c88cdSShubhrajyoti Datta {
339595c88cdSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
340595c88cdSShubhrajyoti Datta 	unsigned long int_freq;
341595c88cdSShubhrajyoti Datta 	u32 m, d, o, div, f;
342595c88cdSShubhrajyoti Datta 	int err;
343595c88cdSShubhrajyoti Datta 
344595c88cdSShubhrajyoti Datta 	err = clk_wzrd_get_divisors(hw, rate, *prate);
345595c88cdSShubhrajyoti Datta 	if (err)
346595c88cdSShubhrajyoti Datta 		return err;
347595c88cdSShubhrajyoti Datta 
348595c88cdSShubhrajyoti Datta 	m = divider->m;
349595c88cdSShubhrajyoti Datta 	d = divider->d;
350595c88cdSShubhrajyoti Datta 	o = divider->o;
351595c88cdSShubhrajyoti Datta 
352595c88cdSShubhrajyoti Datta 	div = d * o;
353595c88cdSShubhrajyoti Datta 	int_freq =  divider_recalc_rate(hw, *prate * m, div, divider->table,
354595c88cdSShubhrajyoti Datta 					divider->flags, divider->width);
355595c88cdSShubhrajyoti Datta 
356595c88cdSShubhrajyoti Datta 	if (rate > int_freq) {
357595c88cdSShubhrajyoti Datta 		f = DIV_ROUND_CLOSEST_ULL(rate * WZRD_FRAC_POINTS, int_freq);
358595c88cdSShubhrajyoti Datta 		rate = DIV_ROUND_CLOSEST(int_freq * f, WZRD_FRAC_POINTS);
359595c88cdSShubhrajyoti Datta 	}
360595c88cdSShubhrajyoti Datta 	return rate;
361595c88cdSShubhrajyoti Datta }
362595c88cdSShubhrajyoti Datta 
363c822490fSShubhrajyoti Datta static const struct clk_ops clk_wzrd_clk_divider_ops = {
364c822490fSShubhrajyoti Datta 	.round_rate = clk_wzrd_round_rate,
365c822490fSShubhrajyoti Datta 	.set_rate = clk_wzrd_dynamic_reconfig,
366c822490fSShubhrajyoti Datta 	.recalc_rate = clk_wzrd_recalc_rate,
367c822490fSShubhrajyoti Datta };
368c822490fSShubhrajyoti Datta 
369595c88cdSShubhrajyoti Datta static const struct clk_ops clk_wzrd_clk_div_all_ops = {
370595c88cdSShubhrajyoti Datta 	.round_rate = clk_wzrd_round_rate_all,
371595c88cdSShubhrajyoti Datta 	.set_rate = clk_wzrd_dynamic_all,
372595c88cdSShubhrajyoti Datta 	.recalc_rate = clk_wzrd_recalc_rate_all,
373595c88cdSShubhrajyoti Datta };
374595c88cdSShubhrajyoti Datta 
clk_wzrd_recalc_ratef(struct clk_hw * hw,unsigned long parent_rate)375c822490fSShubhrajyoti Datta static unsigned long clk_wzrd_recalc_ratef(struct clk_hw *hw,
376c822490fSShubhrajyoti Datta 					   unsigned long parent_rate)
377c822490fSShubhrajyoti Datta {
378c822490fSShubhrajyoti Datta 	unsigned int val;
379c822490fSShubhrajyoti Datta 	u32 div, frac;
380c822490fSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
381c822490fSShubhrajyoti Datta 	void __iomem *div_addr = divider->base + divider->offset;
382c822490fSShubhrajyoti Datta 
383c822490fSShubhrajyoti Datta 	val = readl(div_addr);
384c822490fSShubhrajyoti Datta 	div = val & div_mask(divider->width);
385c822490fSShubhrajyoti Datta 	frac = (val >> WZRD_CLKOUT_FRAC_SHIFT) & WZRD_CLKOUT_FRAC_MASK;
386c822490fSShubhrajyoti Datta 
387c822490fSShubhrajyoti Datta 	return mult_frac(parent_rate, 1000, (div * 1000) + frac);
388c822490fSShubhrajyoti Datta }
389c822490fSShubhrajyoti Datta 
clk_wzrd_dynamic_reconfig_f(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)390c822490fSShubhrajyoti Datta static int clk_wzrd_dynamic_reconfig_f(struct clk_hw *hw, unsigned long rate,
391c822490fSShubhrajyoti Datta 				       unsigned long parent_rate)
392c822490fSShubhrajyoti Datta {
393c822490fSShubhrajyoti Datta 	int err;
394c822490fSShubhrajyoti Datta 	u32 value, pre;
395c822490fSShubhrajyoti Datta 	unsigned long rate_div, f, clockout0_div;
396c822490fSShubhrajyoti Datta 	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
397c822490fSShubhrajyoti Datta 	void __iomem *div_addr = divider->base + divider->offset;
398c822490fSShubhrajyoti Datta 
399dd5e7431SShubhrajyoti Datta 	rate_div = DIV_ROUND_DOWN_ULL(parent_rate * 1000, rate);
400c822490fSShubhrajyoti Datta 	clockout0_div = rate_div / 1000;
401c822490fSShubhrajyoti Datta 
402c822490fSShubhrajyoti Datta 	pre = DIV_ROUND_CLOSEST((parent_rate * 1000), rate);
403c822490fSShubhrajyoti Datta 	f = (u32)(pre - (clockout0_div * 1000));
404c822490fSShubhrajyoti Datta 	f = f & WZRD_CLKOUT_FRAC_MASK;
405c822490fSShubhrajyoti Datta 	f = f << WZRD_CLKOUT_DIVIDE_WIDTH;
406c822490fSShubhrajyoti Datta 
407c822490fSShubhrajyoti Datta 	value = (f  | (clockout0_div & WZRD_CLKOUT_DIVIDE_MASK));
408c822490fSShubhrajyoti Datta 
409c822490fSShubhrajyoti Datta 	/* Set divisor and clear phase offset */
410c822490fSShubhrajyoti Datta 	writel(value, div_addr);
411c822490fSShubhrajyoti Datta 	writel(0x0, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET);
412c822490fSShubhrajyoti Datta 
413c822490fSShubhrajyoti Datta 	/* Check status register */
414c822490fSShubhrajyoti Datta 	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
415c822490fSShubhrajyoti Datta 				 value & WZRD_DR_LOCK_BIT_MASK,
416c822490fSShubhrajyoti Datta 				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
417c822490fSShubhrajyoti Datta 	if (err)
418c822490fSShubhrajyoti Datta 		return err;
419c822490fSShubhrajyoti Datta 
420c822490fSShubhrajyoti Datta 	/* Initiate reconfiguration */
421dd5e7431SShubhrajyoti Datta 	writel(WZRD_DR_BEGIN_DYNA_RECONF_5_2,
422dd5e7431SShubhrajyoti Datta 	       divider->base + WZRD_DR_INIT_REG_OFFSET);
423dd5e7431SShubhrajyoti Datta 	writel(WZRD_DR_BEGIN_DYNA_RECONF1_5_2,
424c822490fSShubhrajyoti Datta 	       divider->base + WZRD_DR_INIT_REG_OFFSET);
425c822490fSShubhrajyoti Datta 
426c822490fSShubhrajyoti Datta 	/* Check status register */
427c822490fSShubhrajyoti Datta 	return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
428c822490fSShubhrajyoti Datta 				value & WZRD_DR_LOCK_BIT_MASK,
429c822490fSShubhrajyoti Datta 				WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
430c822490fSShubhrajyoti Datta }
431c822490fSShubhrajyoti Datta 
clk_wzrd_round_rate_f(struct clk_hw * hw,unsigned long rate,unsigned long * prate)432c822490fSShubhrajyoti Datta static long clk_wzrd_round_rate_f(struct clk_hw *hw, unsigned long rate,
433c822490fSShubhrajyoti Datta 				  unsigned long *prate)
434c822490fSShubhrajyoti Datta {
435c822490fSShubhrajyoti Datta 	return rate;
436c822490fSShubhrajyoti Datta }
437c822490fSShubhrajyoti Datta 
438c822490fSShubhrajyoti Datta static const struct clk_ops clk_wzrd_clk_divider_ops_f = {
439c822490fSShubhrajyoti Datta 	.round_rate = clk_wzrd_round_rate_f,
440c822490fSShubhrajyoti Datta 	.set_rate = clk_wzrd_dynamic_reconfig_f,
441c822490fSShubhrajyoti Datta 	.recalc_rate = clk_wzrd_recalc_ratef,
442c822490fSShubhrajyoti Datta };
443c822490fSShubhrajyoti Datta 
clk_wzrd_register_divf(struct device * dev,const char * name,const char * parent_name,unsigned long flags,void __iomem * base,u16 offset,u8 shift,u8 width,u8 clk_divider_flags,u32 div_type,spinlock_t * lock)444c822490fSShubhrajyoti Datta static struct clk *clk_wzrd_register_divf(struct device *dev,
445c822490fSShubhrajyoti Datta 					  const char *name,
446c822490fSShubhrajyoti Datta 					  const char *parent_name,
447c822490fSShubhrajyoti Datta 					  unsigned long flags,
448c822490fSShubhrajyoti Datta 					  void __iomem *base, u16 offset,
449c822490fSShubhrajyoti Datta 					  u8 shift, u8 width,
450c822490fSShubhrajyoti Datta 					  u8 clk_divider_flags,
451595c88cdSShubhrajyoti Datta 					  u32 div_type,
452c822490fSShubhrajyoti Datta 					  spinlock_t *lock)
453c822490fSShubhrajyoti Datta {
454c822490fSShubhrajyoti Datta 	struct clk_wzrd_divider *div;
455c822490fSShubhrajyoti Datta 	struct clk_hw *hw;
456c822490fSShubhrajyoti Datta 	struct clk_init_data init;
457c822490fSShubhrajyoti Datta 	int ret;
458c822490fSShubhrajyoti Datta 
459c822490fSShubhrajyoti Datta 	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
460c822490fSShubhrajyoti Datta 	if (!div)
461c822490fSShubhrajyoti Datta 		return ERR_PTR(-ENOMEM);
462c822490fSShubhrajyoti Datta 
463c822490fSShubhrajyoti Datta 	init.name = name;
464c822490fSShubhrajyoti Datta 
465c822490fSShubhrajyoti Datta 	init.ops = &clk_wzrd_clk_divider_ops_f;
466c822490fSShubhrajyoti Datta 
467c822490fSShubhrajyoti Datta 	init.flags = flags;
468c822490fSShubhrajyoti Datta 	init.parent_names = &parent_name;
469c822490fSShubhrajyoti Datta 	init.num_parents = 1;
470c822490fSShubhrajyoti Datta 
471c822490fSShubhrajyoti Datta 	div->base = base;
472c822490fSShubhrajyoti Datta 	div->offset = offset;
473c822490fSShubhrajyoti Datta 	div->shift = shift;
474c822490fSShubhrajyoti Datta 	div->width = width;
475c822490fSShubhrajyoti Datta 	div->flags = clk_divider_flags;
476c822490fSShubhrajyoti Datta 	div->lock = lock;
477c822490fSShubhrajyoti Datta 	div->hw.init = &init;
478c822490fSShubhrajyoti Datta 
479c822490fSShubhrajyoti Datta 	hw = &div->hw;
480c822490fSShubhrajyoti Datta 	ret =  devm_clk_hw_register(dev, hw);
481c822490fSShubhrajyoti Datta 	if (ret)
482c822490fSShubhrajyoti Datta 		return ERR_PTR(ret);
483c822490fSShubhrajyoti Datta 
484c822490fSShubhrajyoti Datta 	return hw->clk;
485c822490fSShubhrajyoti Datta }
486c822490fSShubhrajyoti Datta 
clk_wzrd_register_divider(struct device * dev,const char * name,const char * parent_name,unsigned long flags,void __iomem * base,u16 offset,u8 shift,u8 width,u8 clk_divider_flags,u32 div_type,spinlock_t * lock)487c822490fSShubhrajyoti Datta static struct clk *clk_wzrd_register_divider(struct device *dev,
488c822490fSShubhrajyoti Datta 					     const char *name,
489c822490fSShubhrajyoti Datta 					     const char *parent_name,
490c822490fSShubhrajyoti Datta 					     unsigned long flags,
491c822490fSShubhrajyoti Datta 					     void __iomem *base, u16 offset,
492c822490fSShubhrajyoti Datta 					     u8 shift, u8 width,
493c822490fSShubhrajyoti Datta 					     u8 clk_divider_flags,
494595c88cdSShubhrajyoti Datta 					     u32 div_type,
495c822490fSShubhrajyoti Datta 					     spinlock_t *lock)
496c822490fSShubhrajyoti Datta {
497c822490fSShubhrajyoti Datta 	struct clk_wzrd_divider *div;
498c822490fSShubhrajyoti Datta 	struct clk_hw *hw;
499c822490fSShubhrajyoti Datta 	struct clk_init_data init;
500c822490fSShubhrajyoti Datta 	int ret;
501c822490fSShubhrajyoti Datta 
502c822490fSShubhrajyoti Datta 	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
503c822490fSShubhrajyoti Datta 	if (!div)
504c822490fSShubhrajyoti Datta 		return ERR_PTR(-ENOMEM);
505c822490fSShubhrajyoti Datta 
506c822490fSShubhrajyoti Datta 	init.name = name;
507595c88cdSShubhrajyoti Datta 	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
508595c88cdSShubhrajyoti Datta 		init.ops = &clk_divider_ro_ops;
509595c88cdSShubhrajyoti Datta 	else if (div_type == DIV_O)
510c822490fSShubhrajyoti Datta 		init.ops = &clk_wzrd_clk_divider_ops;
511595c88cdSShubhrajyoti Datta 	else
512595c88cdSShubhrajyoti Datta 		init.ops = &clk_wzrd_clk_div_all_ops;
513c822490fSShubhrajyoti Datta 	init.flags = flags;
514c822490fSShubhrajyoti Datta 	init.parent_names =  &parent_name;
515c822490fSShubhrajyoti Datta 	init.num_parents =  1;
516c822490fSShubhrajyoti Datta 
517c822490fSShubhrajyoti Datta 	div->base = base;
518c822490fSShubhrajyoti Datta 	div->offset = offset;
519c822490fSShubhrajyoti Datta 	div->shift = shift;
520c822490fSShubhrajyoti Datta 	div->width = width;
521c822490fSShubhrajyoti Datta 	div->flags = clk_divider_flags;
522c822490fSShubhrajyoti Datta 	div->lock = lock;
523c822490fSShubhrajyoti Datta 	div->hw.init = &init;
524c822490fSShubhrajyoti Datta 
525c822490fSShubhrajyoti Datta 	hw = &div->hw;
526c822490fSShubhrajyoti Datta 	ret = devm_clk_hw_register(dev, hw);
527c822490fSShubhrajyoti Datta 	if (ret)
528c822490fSShubhrajyoti Datta 		return ERR_PTR(ret);
529c822490fSShubhrajyoti Datta 
530c822490fSShubhrajyoti Datta 	return hw->clk;
531c822490fSShubhrajyoti Datta }
532c822490fSShubhrajyoti Datta 
clk_wzrd_clk_notifier(struct notifier_block * nb,unsigned long event,void * data)533c822490fSShubhrajyoti Datta static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
534c822490fSShubhrajyoti Datta 				 void *data)
535c822490fSShubhrajyoti Datta {
536c822490fSShubhrajyoti Datta 	unsigned long max;
537c822490fSShubhrajyoti Datta 	struct clk_notifier_data *ndata = data;
538c822490fSShubhrajyoti Datta 	struct clk_wzrd *clk_wzrd = to_clk_wzrd(nb);
539c822490fSShubhrajyoti Datta 
540c822490fSShubhrajyoti Datta 	if (clk_wzrd->suspended)
541c822490fSShubhrajyoti Datta 		return NOTIFY_OK;
542c822490fSShubhrajyoti Datta 
543c822490fSShubhrajyoti Datta 	if (ndata->clk == clk_wzrd->clk_in1)
544c822490fSShubhrajyoti Datta 		max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1];
545c822490fSShubhrajyoti Datta 	else if (ndata->clk == clk_wzrd->axi_clk)
546c822490fSShubhrajyoti Datta 		max = WZRD_ACLK_MAX_FREQ;
547c822490fSShubhrajyoti Datta 	else
548c822490fSShubhrajyoti Datta 		return NOTIFY_DONE;	/* should never happen */
549c822490fSShubhrajyoti Datta 
550c822490fSShubhrajyoti Datta 	switch (event) {
551c822490fSShubhrajyoti Datta 	case PRE_RATE_CHANGE:
552c822490fSShubhrajyoti Datta 		if (ndata->new_rate > max)
553c822490fSShubhrajyoti Datta 			return NOTIFY_BAD;
554c822490fSShubhrajyoti Datta 		return NOTIFY_OK;
555c822490fSShubhrajyoti Datta 	case POST_RATE_CHANGE:
556c822490fSShubhrajyoti Datta 	case ABORT_RATE_CHANGE:
557c822490fSShubhrajyoti Datta 	default:
558c822490fSShubhrajyoti Datta 		return NOTIFY_DONE;
559c822490fSShubhrajyoti Datta 	}
560c822490fSShubhrajyoti Datta }
561c822490fSShubhrajyoti Datta 
clk_wzrd_suspend(struct device * dev)562c822490fSShubhrajyoti Datta static int __maybe_unused clk_wzrd_suspend(struct device *dev)
563c822490fSShubhrajyoti Datta {
564c822490fSShubhrajyoti Datta 	struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
565c822490fSShubhrajyoti Datta 
566c822490fSShubhrajyoti Datta 	clk_disable_unprepare(clk_wzrd->axi_clk);
567c822490fSShubhrajyoti Datta 	clk_wzrd->suspended = true;
568c822490fSShubhrajyoti Datta 
569c822490fSShubhrajyoti Datta 	return 0;
570c822490fSShubhrajyoti Datta }
571c822490fSShubhrajyoti Datta 
clk_wzrd_resume(struct device * dev)572c822490fSShubhrajyoti Datta static int __maybe_unused clk_wzrd_resume(struct device *dev)
573c822490fSShubhrajyoti Datta {
574c822490fSShubhrajyoti Datta 	int ret;
575c822490fSShubhrajyoti Datta 	struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
576c822490fSShubhrajyoti Datta 
577c822490fSShubhrajyoti Datta 	ret = clk_prepare_enable(clk_wzrd->axi_clk);
578c822490fSShubhrajyoti Datta 	if (ret) {
579c822490fSShubhrajyoti Datta 		dev_err(dev, "unable to enable s_axi_aclk\n");
580c822490fSShubhrajyoti Datta 		return ret;
581c822490fSShubhrajyoti Datta 	}
582c822490fSShubhrajyoti Datta 
583c822490fSShubhrajyoti Datta 	clk_wzrd->suspended = false;
584c822490fSShubhrajyoti Datta 
585c822490fSShubhrajyoti Datta 	return 0;
586c822490fSShubhrajyoti Datta }
587c822490fSShubhrajyoti Datta 
588c822490fSShubhrajyoti Datta static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
589c822490fSShubhrajyoti Datta 			 clk_wzrd_resume);
590c822490fSShubhrajyoti Datta 
clk_wzrd_probe(struct platform_device * pdev)591c822490fSShubhrajyoti Datta static int clk_wzrd_probe(struct platform_device *pdev)
592c822490fSShubhrajyoti Datta {
593c822490fSShubhrajyoti Datta 	int i, ret;
594c822490fSShubhrajyoti Datta 	u32 reg, reg_f, mult;
595c822490fSShubhrajyoti Datta 	unsigned long rate;
596c822490fSShubhrajyoti Datta 	const char *clk_name;
597c822490fSShubhrajyoti Datta 	void __iomem *ctrl_reg;
598c822490fSShubhrajyoti Datta 	struct clk_wzrd *clk_wzrd;
599595c88cdSShubhrajyoti Datta 	const char *clkout_name;
600c822490fSShubhrajyoti Datta 	struct device_node *np = pdev->dev.of_node;
601c822490fSShubhrajyoti Datta 	int nr_outputs;
602c822490fSShubhrajyoti Datta 	unsigned long flags = 0;
603c822490fSShubhrajyoti Datta 
604c822490fSShubhrajyoti Datta 	clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL);
605c822490fSShubhrajyoti Datta 	if (!clk_wzrd)
606c822490fSShubhrajyoti Datta 		return -ENOMEM;
607c822490fSShubhrajyoti Datta 	platform_set_drvdata(pdev, clk_wzrd);
608c822490fSShubhrajyoti Datta 
609c822490fSShubhrajyoti Datta 	clk_wzrd->base = devm_platform_ioremap_resource(pdev, 0);
610c822490fSShubhrajyoti Datta 	if (IS_ERR(clk_wzrd->base))
611c822490fSShubhrajyoti Datta 		return PTR_ERR(clk_wzrd->base);
612c822490fSShubhrajyoti Datta 
613c822490fSShubhrajyoti Datta 	ret = of_property_read_u32(np, "xlnx,speed-grade", &clk_wzrd->speed_grade);
614c822490fSShubhrajyoti Datta 	if (!ret) {
615c822490fSShubhrajyoti Datta 		if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) {
616c822490fSShubhrajyoti Datta 			dev_warn(&pdev->dev, "invalid speed grade '%d'\n",
617c822490fSShubhrajyoti Datta 				 clk_wzrd->speed_grade);
618c822490fSShubhrajyoti Datta 			clk_wzrd->speed_grade = 0;
619c822490fSShubhrajyoti Datta 		}
620c822490fSShubhrajyoti Datta 	}
621c822490fSShubhrajyoti Datta 
622c822490fSShubhrajyoti Datta 	clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1");
623fd30ac84SYang Yingliang 	if (IS_ERR(clk_wzrd->clk_in1))
624fd30ac84SYang Yingliang 		return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->clk_in1),
625fd30ac84SYang Yingliang 				     "clk_in1 not found\n");
626c822490fSShubhrajyoti Datta 
627c822490fSShubhrajyoti Datta 	clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
628fd30ac84SYang Yingliang 	if (IS_ERR(clk_wzrd->axi_clk))
629fd30ac84SYang Yingliang 		return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->axi_clk),
630fd30ac84SYang Yingliang 				     "s_axi_aclk not found\n");
631c822490fSShubhrajyoti Datta 	ret = clk_prepare_enable(clk_wzrd->axi_clk);
632c822490fSShubhrajyoti Datta 	if (ret) {
633c822490fSShubhrajyoti Datta 		dev_err(&pdev->dev, "enabling s_axi_aclk failed\n");
634c822490fSShubhrajyoti Datta 		return ret;
635c822490fSShubhrajyoti Datta 	}
636c822490fSShubhrajyoti Datta 	rate = clk_get_rate(clk_wzrd->axi_clk);
637c822490fSShubhrajyoti Datta 	if (rate > WZRD_ACLK_MAX_FREQ) {
638c822490fSShubhrajyoti Datta 		dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n",
639c822490fSShubhrajyoti Datta 			rate);
640c822490fSShubhrajyoti Datta 		ret = -EINVAL;
641c822490fSShubhrajyoti Datta 		goto err_disable_clk;
642c822490fSShubhrajyoti Datta 	}
643c822490fSShubhrajyoti Datta 
644595c88cdSShubhrajyoti Datta 	ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs);
645595c88cdSShubhrajyoti Datta 	if (ret || nr_outputs > WZRD_NUM_OUTPUTS) {
646595c88cdSShubhrajyoti Datta 		ret = -EINVAL;
647595c88cdSShubhrajyoti Datta 		goto err_disable_clk;
648595c88cdSShubhrajyoti Datta 	}
649595c88cdSShubhrajyoti Datta 
650595c88cdSShubhrajyoti Datta 	clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_out0", dev_name(&pdev->dev));
651*b1356ed1SClaudiu Beznea 	if (!clkout_name) {
652*b1356ed1SClaudiu Beznea 		ret = -ENOMEM;
653*b1356ed1SClaudiu Beznea 		goto err_disable_clk;
654*b1356ed1SClaudiu Beznea 	}
655*b1356ed1SClaudiu Beznea 
656595c88cdSShubhrajyoti Datta 	if (nr_outputs == 1) {
657595c88cdSShubhrajyoti Datta 		clk_wzrd->clkout[0] = clk_wzrd_register_divider
658595c88cdSShubhrajyoti Datta 				(&pdev->dev, clkout_name,
659595c88cdSShubhrajyoti Datta 				__clk_get_name(clk_wzrd->clk_in1), 0,
660595c88cdSShubhrajyoti Datta 				clk_wzrd->base, WZRD_CLK_CFG_REG(3),
661595c88cdSShubhrajyoti Datta 				WZRD_CLKOUT_DIVIDE_SHIFT,
662595c88cdSShubhrajyoti Datta 				WZRD_CLKOUT_DIVIDE_WIDTH,
663595c88cdSShubhrajyoti Datta 				CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
664595c88cdSShubhrajyoti Datta 				DIV_ALL, &clkwzrd_lock);
665595c88cdSShubhrajyoti Datta 
666595c88cdSShubhrajyoti Datta 		goto out;
667595c88cdSShubhrajyoti Datta 	}
668595c88cdSShubhrajyoti Datta 
669c822490fSShubhrajyoti Datta 	reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0));
670c822490fSShubhrajyoti Datta 	reg_f = reg & WZRD_CLKFBOUT_FRAC_MASK;
671c822490fSShubhrajyoti Datta 	reg_f =  reg_f >> WZRD_CLKFBOUT_FRAC_SHIFT;
672c822490fSShubhrajyoti Datta 
673c822490fSShubhrajyoti Datta 	reg = reg & WZRD_CLKFBOUT_MULT_MASK;
674c822490fSShubhrajyoti Datta 	reg =  reg >> WZRD_CLKFBOUT_MULT_SHIFT;
675c822490fSShubhrajyoti Datta 	mult = (reg * 1000) + reg_f;
676595c88cdSShubhrajyoti Datta 	clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
677c822490fSShubhrajyoti Datta 	if (!clk_name) {
678c822490fSShubhrajyoti Datta 		ret = -ENOMEM;
679c822490fSShubhrajyoti Datta 		goto err_disable_clk;
680c822490fSShubhrajyoti Datta 	}
681c822490fSShubhrajyoti Datta 	clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
682c822490fSShubhrajyoti Datta 			(&pdev->dev, clk_name,
683c822490fSShubhrajyoti Datta 			 __clk_get_name(clk_wzrd->clk_in1),
684c822490fSShubhrajyoti Datta 			0, mult, 1000);
685c822490fSShubhrajyoti Datta 	if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
686c822490fSShubhrajyoti Datta 		dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
687c822490fSShubhrajyoti Datta 		ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
688c822490fSShubhrajyoti Datta 		goto err_disable_clk;
689c822490fSShubhrajyoti Datta 	}
690c822490fSShubhrajyoti Datta 
691595c88cdSShubhrajyoti Datta 	clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
692c822490fSShubhrajyoti Datta 	if (!clk_name) {
693c822490fSShubhrajyoti Datta 		ret = -ENOMEM;
694c822490fSShubhrajyoti Datta 		goto err_rm_int_clk;
695c822490fSShubhrajyoti Datta 	}
696c822490fSShubhrajyoti Datta 
697c822490fSShubhrajyoti Datta 	ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(0);
698c822490fSShubhrajyoti Datta 	/* register div */
699c822490fSShubhrajyoti Datta 	clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_divider
700c822490fSShubhrajyoti Datta 			(&pdev->dev, clk_name,
701c822490fSShubhrajyoti Datta 			 __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
702c822490fSShubhrajyoti Datta 			flags, ctrl_reg, 0, 8, CLK_DIVIDER_ONE_BASED |
703c822490fSShubhrajyoti Datta 			CLK_DIVIDER_ALLOW_ZERO, &clkwzrd_lock);
704c822490fSShubhrajyoti Datta 	if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
705c822490fSShubhrajyoti Datta 		dev_err(&pdev->dev, "unable to register divider clock\n");
706c822490fSShubhrajyoti Datta 		ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
707c822490fSShubhrajyoti Datta 		goto err_rm_int_clk;
708c822490fSShubhrajyoti Datta 	}
709c822490fSShubhrajyoti Datta 
710c822490fSShubhrajyoti Datta 	/* register div per output */
711c822490fSShubhrajyoti Datta 	for (i = nr_outputs - 1; i >= 0 ; i--) {
712595c88cdSShubhrajyoti Datta 		clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
713595c88cdSShubhrajyoti Datta 					     "%s_out%d", dev_name(&pdev->dev), i);
714c822490fSShubhrajyoti Datta 		if (!clkout_name) {
715c822490fSShubhrajyoti Datta 			ret = -ENOMEM;
716c822490fSShubhrajyoti Datta 			goto err_rm_int_clk;
717c822490fSShubhrajyoti Datta 		}
718c822490fSShubhrajyoti Datta 
719c822490fSShubhrajyoti Datta 		if (!i)
720c822490fSShubhrajyoti Datta 			clk_wzrd->clkout[i] = clk_wzrd_register_divf
721c822490fSShubhrajyoti Datta 				(&pdev->dev, clkout_name,
722c822490fSShubhrajyoti Datta 				clk_name, flags,
723c822490fSShubhrajyoti Datta 				clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
724c822490fSShubhrajyoti Datta 				WZRD_CLKOUT_DIVIDE_SHIFT,
725c822490fSShubhrajyoti Datta 				WZRD_CLKOUT_DIVIDE_WIDTH,
726c822490fSShubhrajyoti Datta 				CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
727595c88cdSShubhrajyoti Datta 				DIV_O, &clkwzrd_lock);
728c822490fSShubhrajyoti Datta 		else
729c822490fSShubhrajyoti Datta 			clk_wzrd->clkout[i] = clk_wzrd_register_divider
730c822490fSShubhrajyoti Datta 				(&pdev->dev, clkout_name,
731c822490fSShubhrajyoti Datta 				clk_name, 0,
732c822490fSShubhrajyoti Datta 				clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
733c822490fSShubhrajyoti Datta 				WZRD_CLKOUT_DIVIDE_SHIFT,
734c822490fSShubhrajyoti Datta 				WZRD_CLKOUT_DIVIDE_WIDTH,
735c822490fSShubhrajyoti Datta 				CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
736595c88cdSShubhrajyoti Datta 				DIV_O, &clkwzrd_lock);
737c822490fSShubhrajyoti Datta 		if (IS_ERR(clk_wzrd->clkout[i])) {
738c822490fSShubhrajyoti Datta 			int j;
739c822490fSShubhrajyoti Datta 
740c822490fSShubhrajyoti Datta 			for (j = i + 1; j < nr_outputs; j++)
741c822490fSShubhrajyoti Datta 				clk_unregister(clk_wzrd->clkout[j]);
742c822490fSShubhrajyoti Datta 			dev_err(&pdev->dev,
743c822490fSShubhrajyoti Datta 				"unable to register divider clock\n");
744c822490fSShubhrajyoti Datta 			ret = PTR_ERR(clk_wzrd->clkout[i]);
745c822490fSShubhrajyoti Datta 			goto err_rm_int_clks;
746c822490fSShubhrajyoti Datta 		}
747c822490fSShubhrajyoti Datta 	}
748c822490fSShubhrajyoti Datta 
749595c88cdSShubhrajyoti Datta out:
750c822490fSShubhrajyoti Datta 	clk_wzrd->clk_data.clks = clk_wzrd->clkout;
751c822490fSShubhrajyoti Datta 	clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout);
752c822490fSShubhrajyoti Datta 	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data);
753c822490fSShubhrajyoti Datta 
754c822490fSShubhrajyoti Datta 	if (clk_wzrd->speed_grade) {
755c822490fSShubhrajyoti Datta 		clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier;
756c822490fSShubhrajyoti Datta 
757c822490fSShubhrajyoti Datta 		ret = clk_notifier_register(clk_wzrd->clk_in1,
758c822490fSShubhrajyoti Datta 					    &clk_wzrd->nb);
759c822490fSShubhrajyoti Datta 		if (ret)
760c822490fSShubhrajyoti Datta 			dev_warn(&pdev->dev,
761c822490fSShubhrajyoti Datta 				 "unable to register clock notifier\n");
762c822490fSShubhrajyoti Datta 
763c822490fSShubhrajyoti Datta 		ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb);
764c822490fSShubhrajyoti Datta 		if (ret)
765c822490fSShubhrajyoti Datta 			dev_warn(&pdev->dev,
766c822490fSShubhrajyoti Datta 				 "unable to register clock notifier\n");
767c822490fSShubhrajyoti Datta 	}
768c822490fSShubhrajyoti Datta 
769c822490fSShubhrajyoti Datta 	return 0;
770c822490fSShubhrajyoti Datta 
771c822490fSShubhrajyoti Datta err_rm_int_clks:
772c822490fSShubhrajyoti Datta 	clk_unregister(clk_wzrd->clks_internal[1]);
773c822490fSShubhrajyoti Datta err_rm_int_clk:
774c822490fSShubhrajyoti Datta 	clk_unregister(clk_wzrd->clks_internal[0]);
775c822490fSShubhrajyoti Datta err_disable_clk:
776c822490fSShubhrajyoti Datta 	clk_disable_unprepare(clk_wzrd->axi_clk);
777c822490fSShubhrajyoti Datta 
778c822490fSShubhrajyoti Datta 	return ret;
779c822490fSShubhrajyoti Datta }
780c822490fSShubhrajyoti Datta 
clk_wzrd_remove(struct platform_device * pdev)781ce1c5f84SUwe Kleine-König static void clk_wzrd_remove(struct platform_device *pdev)
782c822490fSShubhrajyoti Datta {
783c822490fSShubhrajyoti Datta 	int i;
784c822490fSShubhrajyoti Datta 	struct clk_wzrd *clk_wzrd = platform_get_drvdata(pdev);
785c822490fSShubhrajyoti Datta 
786c822490fSShubhrajyoti Datta 	of_clk_del_provider(pdev->dev.of_node);
787c822490fSShubhrajyoti Datta 
788c822490fSShubhrajyoti Datta 	for (i = 0; i < WZRD_NUM_OUTPUTS; i++)
789c822490fSShubhrajyoti Datta 		clk_unregister(clk_wzrd->clkout[i]);
790c822490fSShubhrajyoti Datta 	for (i = 0; i < wzrd_clk_int_max; i++)
791c822490fSShubhrajyoti Datta 		clk_unregister(clk_wzrd->clks_internal[i]);
792c822490fSShubhrajyoti Datta 
793c822490fSShubhrajyoti Datta 	if (clk_wzrd->speed_grade) {
794c822490fSShubhrajyoti Datta 		clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb);
795c822490fSShubhrajyoti Datta 		clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb);
796c822490fSShubhrajyoti Datta 	}
797c822490fSShubhrajyoti Datta 
798c822490fSShubhrajyoti Datta 	clk_disable_unprepare(clk_wzrd->axi_clk);
799c822490fSShubhrajyoti Datta }
800c822490fSShubhrajyoti Datta 
801c822490fSShubhrajyoti Datta static const struct of_device_id clk_wzrd_ids[] = {
802c822490fSShubhrajyoti Datta 	{ .compatible = "xlnx,clocking-wizard" },
803e8db788dSShubhrajyoti Datta 	{ .compatible = "xlnx,clocking-wizard-v5.2" },
804e8db788dSShubhrajyoti Datta 	{ .compatible = "xlnx,clocking-wizard-v6.0" },
805c822490fSShubhrajyoti Datta 	{ },
806c822490fSShubhrajyoti Datta };
807c822490fSShubhrajyoti Datta MODULE_DEVICE_TABLE(of, clk_wzrd_ids);
808c822490fSShubhrajyoti Datta 
809c822490fSShubhrajyoti Datta static struct platform_driver clk_wzrd_driver = {
810c822490fSShubhrajyoti Datta 	.driver = {
811c822490fSShubhrajyoti Datta 		.name = "clk-wizard",
812c822490fSShubhrajyoti Datta 		.of_match_table = clk_wzrd_ids,
813c822490fSShubhrajyoti Datta 		.pm = &clk_wzrd_dev_pm_ops,
814c822490fSShubhrajyoti Datta 	},
815c822490fSShubhrajyoti Datta 	.probe = clk_wzrd_probe,
816ce1c5f84SUwe Kleine-König 	.remove_new = clk_wzrd_remove,
817c822490fSShubhrajyoti Datta };
818c822490fSShubhrajyoti Datta module_platform_driver(clk_wzrd_driver);
819c822490fSShubhrajyoti Datta 
820c822490fSShubhrajyoti Datta MODULE_LICENSE("GPL");
821c822490fSShubhrajyoti Datta MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
822c822490fSShubhrajyoti Datta MODULE_DESCRIPTION("Driver for the Xilinx Clocking Wizard IP core");
823