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