xref: /linux/drivers/clk/microchip/clk-pic32.c (revision 2ed2e359dea752e7758d29a423033031a0b96584)
1*b82e98bfSConor Dooley // SPDX-License-Identifier: GPL-2.0-only
2*b82e98bfSConor Dooley /*
3*b82e98bfSConor Dooley  * Purna Chandra Mandal,<purna.mandal@microchip.com>
4*b82e98bfSConor Dooley  * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
5*b82e98bfSConor Dooley  */
6*b82e98bfSConor Dooley #include <linux/clk-provider.h>
7*b82e98bfSConor Dooley #include <linux/delay.h>
8*b82e98bfSConor Dooley #include <linux/device.h>
9*b82e98bfSConor Dooley #include <linux/interrupt.h>
10*b82e98bfSConor Dooley #include <linux/io.h>
11*b82e98bfSConor Dooley #include <linux/iopoll.h>
12*b82e98bfSConor Dooley #include <linux/platform_data/pic32.h>
13*b82e98bfSConor Dooley 
14*b82e98bfSConor Dooley #include "clk-pic32.h"
15*b82e98bfSConor Dooley 
16*b82e98bfSConor Dooley /* OSCCON Reg fields */
17*b82e98bfSConor Dooley #define OSC_CUR_MASK		0x07
18*b82e98bfSConor Dooley #define OSC_CUR_SHIFT		12
19*b82e98bfSConor Dooley #define OSC_NEW_MASK		0x07
20*b82e98bfSConor Dooley #define OSC_NEW_SHIFT		8
21*b82e98bfSConor Dooley #define OSC_SWEN		BIT(0)
22*b82e98bfSConor Dooley 
23*b82e98bfSConor Dooley /* SPLLCON Reg fields */
24*b82e98bfSConor Dooley #define PLL_RANGE_MASK		0x07
25*b82e98bfSConor Dooley #define PLL_RANGE_SHIFT		0
26*b82e98bfSConor Dooley #define PLL_ICLK_MASK		0x01
27*b82e98bfSConor Dooley #define PLL_ICLK_SHIFT		7
28*b82e98bfSConor Dooley #define PLL_IDIV_MASK		0x07
29*b82e98bfSConor Dooley #define PLL_IDIV_SHIFT		8
30*b82e98bfSConor Dooley #define PLL_ODIV_MASK		0x07
31*b82e98bfSConor Dooley #define PLL_ODIV_SHIFT		24
32*b82e98bfSConor Dooley #define PLL_MULT_MASK		0x7F
33*b82e98bfSConor Dooley #define PLL_MULT_SHIFT		16
34*b82e98bfSConor Dooley #define PLL_MULT_MAX		128
35*b82e98bfSConor Dooley #define PLL_ODIV_MIN		1
36*b82e98bfSConor Dooley #define PLL_ODIV_MAX		5
37*b82e98bfSConor Dooley 
38*b82e98bfSConor Dooley /* Peripheral Bus Clock Reg Fields */
39*b82e98bfSConor Dooley #define PB_DIV_MASK		0x7f
40*b82e98bfSConor Dooley #define PB_DIV_SHIFT		0
41*b82e98bfSConor Dooley #define PB_DIV_READY		BIT(11)
42*b82e98bfSConor Dooley #define PB_DIV_ENABLE		BIT(15)
43*b82e98bfSConor Dooley #define PB_DIV_MAX		128
44*b82e98bfSConor Dooley #define PB_DIV_MIN		0
45*b82e98bfSConor Dooley 
46*b82e98bfSConor Dooley /* Reference Oscillator Control Reg fields */
47*b82e98bfSConor Dooley #define REFO_SEL_MASK		0x0f
48*b82e98bfSConor Dooley #define REFO_SEL_SHIFT		0
49*b82e98bfSConor Dooley #define REFO_ACTIVE		BIT(8)
50*b82e98bfSConor Dooley #define REFO_DIVSW_EN		BIT(9)
51*b82e98bfSConor Dooley #define REFO_OE			BIT(12)
52*b82e98bfSConor Dooley #define REFO_ON			BIT(15)
53*b82e98bfSConor Dooley #define REFO_DIV_SHIFT		16
54*b82e98bfSConor Dooley #define REFO_DIV_MASK		0x7fff
55*b82e98bfSConor Dooley 
56*b82e98bfSConor Dooley /* Reference Oscillator Trim Register Fields */
57*b82e98bfSConor Dooley #define REFO_TRIM_REG		0x10
58*b82e98bfSConor Dooley #define REFO_TRIM_MASK		0x1ff
59*b82e98bfSConor Dooley #define REFO_TRIM_SHIFT		23
60*b82e98bfSConor Dooley #define REFO_TRIM_MAX		511
61*b82e98bfSConor Dooley 
62*b82e98bfSConor Dooley /* Mux Slew Control Register fields */
63*b82e98bfSConor Dooley #define SLEW_BUSY		BIT(0)
64*b82e98bfSConor Dooley #define SLEW_DOWNEN		BIT(1)
65*b82e98bfSConor Dooley #define SLEW_UPEN		BIT(2)
66*b82e98bfSConor Dooley #define SLEW_DIV		0x07
67*b82e98bfSConor Dooley #define SLEW_DIV_SHIFT		8
68*b82e98bfSConor Dooley #define SLEW_SYSDIV		0x0f
69*b82e98bfSConor Dooley #define SLEW_SYSDIV_SHIFT	20
70*b82e98bfSConor Dooley 
71*b82e98bfSConor Dooley /* Clock Poll Timeout */
72*b82e98bfSConor Dooley #define LOCK_TIMEOUT_US         USEC_PER_MSEC
73*b82e98bfSConor Dooley 
74*b82e98bfSConor Dooley /* SoC specific clock needed during SPLL clock rate switch */
75*b82e98bfSConor Dooley static struct clk_hw *pic32_sclk_hw;
76*b82e98bfSConor Dooley 
77*b82e98bfSConor Dooley /* add instruction pipeline delay while CPU clock is in-transition. */
78*b82e98bfSConor Dooley #define cpu_nop5()			\
79*b82e98bfSConor Dooley do {					\
80*b82e98bfSConor Dooley 	__asm__ __volatile__("nop");	\
81*b82e98bfSConor Dooley 	__asm__ __volatile__("nop");	\
82*b82e98bfSConor Dooley 	__asm__ __volatile__("nop");	\
83*b82e98bfSConor Dooley 	__asm__ __volatile__("nop");	\
84*b82e98bfSConor Dooley 	__asm__ __volatile__("nop");	\
85*b82e98bfSConor Dooley } while (0)
86*b82e98bfSConor Dooley 
87*b82e98bfSConor Dooley /* Perpheral bus clocks */
88*b82e98bfSConor Dooley struct pic32_periph_clk {
89*b82e98bfSConor Dooley 	struct clk_hw hw;
90*b82e98bfSConor Dooley 	void __iomem *ctrl_reg;
91*b82e98bfSConor Dooley 	struct pic32_clk_common *core;
92*b82e98bfSConor Dooley };
93*b82e98bfSConor Dooley 
94*b82e98bfSConor Dooley #define clkhw_to_pbclk(_hw)	container_of(_hw, struct pic32_periph_clk, hw)
95*b82e98bfSConor Dooley 
pbclk_is_enabled(struct clk_hw * hw)96*b82e98bfSConor Dooley static int pbclk_is_enabled(struct clk_hw *hw)
97*b82e98bfSConor Dooley {
98*b82e98bfSConor Dooley 	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
99*b82e98bfSConor Dooley 
100*b82e98bfSConor Dooley 	return readl(pb->ctrl_reg) & PB_DIV_ENABLE;
101*b82e98bfSConor Dooley }
102*b82e98bfSConor Dooley 
pbclk_enable(struct clk_hw * hw)103*b82e98bfSConor Dooley static int pbclk_enable(struct clk_hw *hw)
104*b82e98bfSConor Dooley {
105*b82e98bfSConor Dooley 	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
106*b82e98bfSConor Dooley 
107*b82e98bfSConor Dooley 	writel(PB_DIV_ENABLE, PIC32_SET(pb->ctrl_reg));
108*b82e98bfSConor Dooley 	return 0;
109*b82e98bfSConor Dooley }
110*b82e98bfSConor Dooley 
pbclk_disable(struct clk_hw * hw)111*b82e98bfSConor Dooley static void pbclk_disable(struct clk_hw *hw)
112*b82e98bfSConor Dooley {
113*b82e98bfSConor Dooley 	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
114*b82e98bfSConor Dooley 
115*b82e98bfSConor Dooley 	writel(PB_DIV_ENABLE, PIC32_CLR(pb->ctrl_reg));
116*b82e98bfSConor Dooley }
117*b82e98bfSConor Dooley 
calc_best_divided_rate(unsigned long rate,unsigned long parent_rate,u32 divider_max,u32 divider_min)118*b82e98bfSConor Dooley static unsigned long calc_best_divided_rate(unsigned long rate,
119*b82e98bfSConor Dooley 					    unsigned long parent_rate,
120*b82e98bfSConor Dooley 					    u32 divider_max,
121*b82e98bfSConor Dooley 					    u32 divider_min)
122*b82e98bfSConor Dooley {
123*b82e98bfSConor Dooley 	unsigned long divided_rate, divided_rate_down, best_rate;
124*b82e98bfSConor Dooley 	unsigned long div, div_up;
125*b82e98bfSConor Dooley 
126*b82e98bfSConor Dooley 	/* eq. clk_rate = parent_rate / divider.
127*b82e98bfSConor Dooley 	 *
128*b82e98bfSConor Dooley 	 * Find best divider to produce closest of target divided rate.
129*b82e98bfSConor Dooley 	 */
130*b82e98bfSConor Dooley 	div = parent_rate / rate;
131*b82e98bfSConor Dooley 	div = clamp_val(div, divider_min, divider_max);
132*b82e98bfSConor Dooley 	div_up = clamp_val(div + 1, divider_min, divider_max);
133*b82e98bfSConor Dooley 
134*b82e98bfSConor Dooley 	divided_rate = parent_rate / div;
135*b82e98bfSConor Dooley 	divided_rate_down = parent_rate / div_up;
136*b82e98bfSConor Dooley 	if (abs(rate - divided_rate_down) < abs(rate - divided_rate))
137*b82e98bfSConor Dooley 		best_rate = divided_rate_down;
138*b82e98bfSConor Dooley 	else
139*b82e98bfSConor Dooley 		best_rate = divided_rate;
140*b82e98bfSConor Dooley 
141*b82e98bfSConor Dooley 	return best_rate;
142*b82e98bfSConor Dooley }
143*b82e98bfSConor Dooley 
pbclk_read_pbdiv(struct pic32_periph_clk * pb)144*b82e98bfSConor Dooley static inline u32 pbclk_read_pbdiv(struct pic32_periph_clk *pb)
145*b82e98bfSConor Dooley {
146*b82e98bfSConor Dooley 	return ((readl(pb->ctrl_reg) >> PB_DIV_SHIFT) & PB_DIV_MASK) + 1;
147*b82e98bfSConor Dooley }
148*b82e98bfSConor Dooley 
pbclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)149*b82e98bfSConor Dooley static unsigned long pbclk_recalc_rate(struct clk_hw *hw,
150*b82e98bfSConor Dooley 				       unsigned long parent_rate)
151*b82e98bfSConor Dooley {
152*b82e98bfSConor Dooley 	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
153*b82e98bfSConor Dooley 
154*b82e98bfSConor Dooley 	return parent_rate / pbclk_read_pbdiv(pb);
155*b82e98bfSConor Dooley }
156*b82e98bfSConor Dooley 
pbclk_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)157*b82e98bfSConor Dooley static int pbclk_determine_rate(struct clk_hw *hw,
158*b82e98bfSConor Dooley 				struct clk_rate_request *req)
159*b82e98bfSConor Dooley {
160*b82e98bfSConor Dooley 	req->rate = calc_best_divided_rate(req->rate, req->best_parent_rate,
161*b82e98bfSConor Dooley 					   PB_DIV_MAX, PB_DIV_MIN);
162*b82e98bfSConor Dooley 
163*b82e98bfSConor Dooley 	return 0;
164*b82e98bfSConor Dooley }
165*b82e98bfSConor Dooley 
pbclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)166*b82e98bfSConor Dooley static int pbclk_set_rate(struct clk_hw *hw, unsigned long rate,
167*b82e98bfSConor Dooley 			  unsigned long parent_rate)
168*b82e98bfSConor Dooley {
169*b82e98bfSConor Dooley 	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
170*b82e98bfSConor Dooley 	unsigned long flags;
171*b82e98bfSConor Dooley 	u32 v, div;
172*b82e98bfSConor Dooley 	int err;
173*b82e98bfSConor Dooley 
174*b82e98bfSConor Dooley 	/* check & wait for DIV_READY */
175*b82e98bfSConor Dooley 	err = readl_poll_timeout(pb->ctrl_reg, v, v & PB_DIV_READY,
176*b82e98bfSConor Dooley 				 1, LOCK_TIMEOUT_US);
177*b82e98bfSConor Dooley 	if (err)
178*b82e98bfSConor Dooley 		return err;
179*b82e98bfSConor Dooley 
180*b82e98bfSConor Dooley 	/* calculate clkdiv and best rate */
181*b82e98bfSConor Dooley 	div = DIV_ROUND_CLOSEST(parent_rate, rate);
182*b82e98bfSConor Dooley 
183*b82e98bfSConor Dooley 	spin_lock_irqsave(&pb->core->reg_lock, flags);
184*b82e98bfSConor Dooley 
185*b82e98bfSConor Dooley 	/* apply new div */
186*b82e98bfSConor Dooley 	v = readl(pb->ctrl_reg);
187*b82e98bfSConor Dooley 	v &= ~PB_DIV_MASK;
188*b82e98bfSConor Dooley 	v |= (div - 1);
189*b82e98bfSConor Dooley 
190*b82e98bfSConor Dooley 	pic32_syskey_unlock();
191*b82e98bfSConor Dooley 
192*b82e98bfSConor Dooley 	writel(v, pb->ctrl_reg);
193*b82e98bfSConor Dooley 
194*b82e98bfSConor Dooley 	spin_unlock_irqrestore(&pb->core->reg_lock, flags);
195*b82e98bfSConor Dooley 
196*b82e98bfSConor Dooley 	/* wait again for DIV_READY */
197*b82e98bfSConor Dooley 	err = readl_poll_timeout(pb->ctrl_reg, v, v & PB_DIV_READY,
198*b82e98bfSConor Dooley 				 1, LOCK_TIMEOUT_US);
199*b82e98bfSConor Dooley 	if (err)
200*b82e98bfSConor Dooley 		return err;
201*b82e98bfSConor Dooley 
202*b82e98bfSConor Dooley 	/* confirm that new div is applied correctly */
203*b82e98bfSConor Dooley 	return (pbclk_read_pbdiv(pb) == div) ? 0 : -EBUSY;
204*b82e98bfSConor Dooley }
205*b82e98bfSConor Dooley 
206*b82e98bfSConor Dooley const struct clk_ops pic32_pbclk_ops = {
207*b82e98bfSConor Dooley 	.enable		= pbclk_enable,
208*b82e98bfSConor Dooley 	.disable	= pbclk_disable,
209*b82e98bfSConor Dooley 	.is_enabled	= pbclk_is_enabled,
210*b82e98bfSConor Dooley 	.recalc_rate	= pbclk_recalc_rate,
211*b82e98bfSConor Dooley 	.determine_rate = pbclk_determine_rate,
212*b82e98bfSConor Dooley 	.set_rate	= pbclk_set_rate,
213*b82e98bfSConor Dooley };
214*b82e98bfSConor Dooley 
pic32_periph_clk_register(const struct pic32_periph_clk_data * desc,struct pic32_clk_common * core)215*b82e98bfSConor Dooley struct clk *pic32_periph_clk_register(const struct pic32_periph_clk_data *desc,
216*b82e98bfSConor Dooley 				      struct pic32_clk_common *core)
217*b82e98bfSConor Dooley {
218*b82e98bfSConor Dooley 	struct pic32_periph_clk *pbclk;
219*b82e98bfSConor Dooley 	struct clk *clk;
220*b82e98bfSConor Dooley 
221*b82e98bfSConor Dooley 	pbclk = devm_kzalloc(core->dev, sizeof(*pbclk), GFP_KERNEL);
222*b82e98bfSConor Dooley 	if (!pbclk)
223*b82e98bfSConor Dooley 		return ERR_PTR(-ENOMEM);
224*b82e98bfSConor Dooley 
225*b82e98bfSConor Dooley 	pbclk->hw.init = &desc->init_data;
226*b82e98bfSConor Dooley 	pbclk->core = core;
227*b82e98bfSConor Dooley 	pbclk->ctrl_reg = desc->ctrl_reg + core->iobase;
228*b82e98bfSConor Dooley 
229*b82e98bfSConor Dooley 	clk = devm_clk_register(core->dev, &pbclk->hw);
230*b82e98bfSConor Dooley 	if (IS_ERR(clk)) {
231*b82e98bfSConor Dooley 		dev_err(core->dev, "%s: clk_register() failed\n", __func__);
232*b82e98bfSConor Dooley 		devm_kfree(core->dev, pbclk);
233*b82e98bfSConor Dooley 	}
234*b82e98bfSConor Dooley 
235*b82e98bfSConor Dooley 	return clk;
236*b82e98bfSConor Dooley }
237*b82e98bfSConor Dooley 
238*b82e98bfSConor Dooley /* Reference oscillator operations */
239*b82e98bfSConor Dooley struct pic32_ref_osc {
240*b82e98bfSConor Dooley 	struct clk_hw hw;
241*b82e98bfSConor Dooley 	void __iomem *ctrl_reg;
242*b82e98bfSConor Dooley 	const u32 *parent_map;
243*b82e98bfSConor Dooley 	struct pic32_clk_common *core;
244*b82e98bfSConor Dooley };
245*b82e98bfSConor Dooley 
246*b82e98bfSConor Dooley #define clkhw_to_refosc(_hw)	container_of(_hw, struct pic32_ref_osc, hw)
247*b82e98bfSConor Dooley 
roclk_is_enabled(struct clk_hw * hw)248*b82e98bfSConor Dooley static int roclk_is_enabled(struct clk_hw *hw)
249*b82e98bfSConor Dooley {
250*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
251*b82e98bfSConor Dooley 
252*b82e98bfSConor Dooley 	return readl(refo->ctrl_reg) & REFO_ON;
253*b82e98bfSConor Dooley }
254*b82e98bfSConor Dooley 
roclk_enable(struct clk_hw * hw)255*b82e98bfSConor Dooley static int roclk_enable(struct clk_hw *hw)
256*b82e98bfSConor Dooley {
257*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
258*b82e98bfSConor Dooley 
259*b82e98bfSConor Dooley 	writel(REFO_ON | REFO_OE, PIC32_SET(refo->ctrl_reg));
260*b82e98bfSConor Dooley 	return 0;
261*b82e98bfSConor Dooley }
262*b82e98bfSConor Dooley 
roclk_disable(struct clk_hw * hw)263*b82e98bfSConor Dooley static void roclk_disable(struct clk_hw *hw)
264*b82e98bfSConor Dooley {
265*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
266*b82e98bfSConor Dooley 
267*b82e98bfSConor Dooley 	writel(REFO_ON | REFO_OE, PIC32_CLR(refo->ctrl_reg));
268*b82e98bfSConor Dooley }
269*b82e98bfSConor Dooley 
roclk_init(struct clk_hw * hw)270*b82e98bfSConor Dooley static int roclk_init(struct clk_hw *hw)
271*b82e98bfSConor Dooley {
272*b82e98bfSConor Dooley 	/* initialize clock in disabled state */
273*b82e98bfSConor Dooley 	roclk_disable(hw);
274*b82e98bfSConor Dooley 
275*b82e98bfSConor Dooley 	return 0;
276*b82e98bfSConor Dooley }
277*b82e98bfSConor Dooley 
roclk_get_parent(struct clk_hw * hw)278*b82e98bfSConor Dooley static u8 roclk_get_parent(struct clk_hw *hw)
279*b82e98bfSConor Dooley {
280*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
281*b82e98bfSConor Dooley 	u32 v, i;
282*b82e98bfSConor Dooley 
283*b82e98bfSConor Dooley 	v = (readl(refo->ctrl_reg) >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
284*b82e98bfSConor Dooley 
285*b82e98bfSConor Dooley 	if (refo->parent_map) {
286*b82e98bfSConor Dooley 		for (i = 0; i < clk_hw_get_num_parents(hw); i++)
287*b82e98bfSConor Dooley 			if (refo->parent_map[i] == v)
288*b82e98bfSConor Dooley 				return i;
289*b82e98bfSConor Dooley 	}
290*b82e98bfSConor Dooley 
291*b82e98bfSConor Dooley 	return v;
292*b82e98bfSConor Dooley }
293*b82e98bfSConor Dooley 
roclk_calc_rate(unsigned long parent_rate,u32 rodiv,u32 rotrim)294*b82e98bfSConor Dooley static unsigned long roclk_calc_rate(unsigned long parent_rate,
295*b82e98bfSConor Dooley 				     u32 rodiv, u32 rotrim)
296*b82e98bfSConor Dooley {
297*b82e98bfSConor Dooley 	u64 rate64;
298*b82e98bfSConor Dooley 
299*b82e98bfSConor Dooley 	/* fout = fin / [2 * {div + (trim / 512)}]
300*b82e98bfSConor Dooley 	 *	= fin * 512 / [1024 * div + 2 * trim]
301*b82e98bfSConor Dooley 	 *	= fin * 256 / (512 * div + trim)
302*b82e98bfSConor Dooley 	 *	= (fin << 8) / ((div << 9) + trim)
303*b82e98bfSConor Dooley 	 */
304*b82e98bfSConor Dooley 	if (rotrim) {
305*b82e98bfSConor Dooley 		rodiv = (rodiv << 9) + rotrim;
306*b82e98bfSConor Dooley 		rate64 = parent_rate;
307*b82e98bfSConor Dooley 		rate64 <<= 8;
308*b82e98bfSConor Dooley 		do_div(rate64, rodiv);
309*b82e98bfSConor Dooley 	} else if (rodiv) {
310*b82e98bfSConor Dooley 		rate64 = parent_rate / (rodiv << 1);
311*b82e98bfSConor Dooley 	} else {
312*b82e98bfSConor Dooley 		rate64 = parent_rate;
313*b82e98bfSConor Dooley 	}
314*b82e98bfSConor Dooley 	return rate64;
315*b82e98bfSConor Dooley }
316*b82e98bfSConor Dooley 
roclk_calc_div_trim(unsigned long rate,unsigned long parent_rate,u32 * rodiv_p,u32 * rotrim_p)317*b82e98bfSConor Dooley static void roclk_calc_div_trim(unsigned long rate,
318*b82e98bfSConor Dooley 				unsigned long parent_rate,
319*b82e98bfSConor Dooley 				u32 *rodiv_p, u32 *rotrim_p)
320*b82e98bfSConor Dooley {
321*b82e98bfSConor Dooley 	u32 div, rotrim, rodiv;
322*b82e98bfSConor Dooley 	u64 frac;
323*b82e98bfSConor Dooley 
324*b82e98bfSConor Dooley 	/* Find integer approximation of floating-point arithmetic.
325*b82e98bfSConor Dooley 	 *      fout = fin / [2 * {rodiv + (rotrim / 512)}] ... (1)
326*b82e98bfSConor Dooley 	 * i.e. fout = fin / 2 * DIV
327*b82e98bfSConor Dooley 	 *      whereas DIV = rodiv + (rotrim / 512)
328*b82e98bfSConor Dooley 	 *
329*b82e98bfSConor Dooley 	 * Since kernel does not perform floating-point arithmetic so
330*b82e98bfSConor Dooley 	 * (rotrim/512) will be zero. And DIV & rodiv will result same.
331*b82e98bfSConor Dooley 	 *
332*b82e98bfSConor Dooley 	 * ie. fout = (fin * 256) / [(512 * rodiv) + rotrim]  ... from (1)
333*b82e98bfSConor Dooley 	 * ie. rotrim = ((fin * 256) / fout) - (512 * DIV)
334*b82e98bfSConor Dooley 	 */
335*b82e98bfSConor Dooley 	if (parent_rate <= rate) {
336*b82e98bfSConor Dooley 		div = 0;
337*b82e98bfSConor Dooley 		frac = 0;
338*b82e98bfSConor Dooley 		rodiv = 0;
339*b82e98bfSConor Dooley 		rotrim = 0;
340*b82e98bfSConor Dooley 	} else {
341*b82e98bfSConor Dooley 		div = parent_rate / (rate << 1);
342*b82e98bfSConor Dooley 		frac = parent_rate;
343*b82e98bfSConor Dooley 		frac <<= 8;
344*b82e98bfSConor Dooley 		do_div(frac, rate);
345*b82e98bfSConor Dooley 		frac -= (u64)(div << 9);
346*b82e98bfSConor Dooley 
347*b82e98bfSConor Dooley 		rodiv = (div > REFO_DIV_MASK) ? REFO_DIV_MASK : div;
348*b82e98bfSConor Dooley 		rotrim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : frac;
349*b82e98bfSConor Dooley 	}
350*b82e98bfSConor Dooley 
351*b82e98bfSConor Dooley 	if (rodiv_p)
352*b82e98bfSConor Dooley 		*rodiv_p = rodiv;
353*b82e98bfSConor Dooley 
354*b82e98bfSConor Dooley 	if (rotrim_p)
355*b82e98bfSConor Dooley 		*rotrim_p = rotrim;
356*b82e98bfSConor Dooley }
357*b82e98bfSConor Dooley 
roclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)358*b82e98bfSConor Dooley static unsigned long roclk_recalc_rate(struct clk_hw *hw,
359*b82e98bfSConor Dooley 				       unsigned long parent_rate)
360*b82e98bfSConor Dooley {
361*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
362*b82e98bfSConor Dooley 	u32 v, rodiv, rotrim;
363*b82e98bfSConor Dooley 
364*b82e98bfSConor Dooley 	/* get rodiv */
365*b82e98bfSConor Dooley 	v = readl(refo->ctrl_reg);
366*b82e98bfSConor Dooley 	rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
367*b82e98bfSConor Dooley 
368*b82e98bfSConor Dooley 	/* get trim */
369*b82e98bfSConor Dooley 	v = readl(refo->ctrl_reg + REFO_TRIM_REG);
370*b82e98bfSConor Dooley 	rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
371*b82e98bfSConor Dooley 
372*b82e98bfSConor Dooley 	return roclk_calc_rate(parent_rate, rodiv, rotrim);
373*b82e98bfSConor Dooley }
374*b82e98bfSConor Dooley 
roclk_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)375*b82e98bfSConor Dooley static int roclk_determine_rate(struct clk_hw *hw,
376*b82e98bfSConor Dooley 				struct clk_rate_request *req)
377*b82e98bfSConor Dooley {
378*b82e98bfSConor Dooley 	struct clk_hw *parent_clk, *best_parent_clk = NULL;
379*b82e98bfSConor Dooley 	unsigned int i, delta, best_delta = -1;
380*b82e98bfSConor Dooley 	unsigned long parent_rate, best_parent_rate = 0;
381*b82e98bfSConor Dooley 	unsigned long best = 0, nearest_rate;
382*b82e98bfSConor Dooley 
383*b82e98bfSConor Dooley 	/* find a parent which can generate nearest clkrate >= rate */
384*b82e98bfSConor Dooley 	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
385*b82e98bfSConor Dooley 		u32 rotrim, rodiv;
386*b82e98bfSConor Dooley 
387*b82e98bfSConor Dooley 		/* get parent */
388*b82e98bfSConor Dooley 		parent_clk = clk_hw_get_parent_by_index(hw, i);
389*b82e98bfSConor Dooley 		if (!parent_clk)
390*b82e98bfSConor Dooley 			continue;
391*b82e98bfSConor Dooley 
392*b82e98bfSConor Dooley 		/* skip if parent runs slower than target rate */
393*b82e98bfSConor Dooley 		parent_rate = clk_hw_get_rate(parent_clk);
394*b82e98bfSConor Dooley 		if (req->rate > parent_rate)
395*b82e98bfSConor Dooley 			continue;
396*b82e98bfSConor Dooley 
397*b82e98bfSConor Dooley 		/* calculate dividers for new rate */
398*b82e98bfSConor Dooley 		roclk_calc_div_trim(req->rate, req->best_parent_rate, &rodiv, &rotrim);
399*b82e98bfSConor Dooley 
400*b82e98bfSConor Dooley 		/* caclulate new rate (rounding) based on new rodiv & rotrim */
401*b82e98bfSConor Dooley 		nearest_rate = roclk_calc_rate(req->best_parent_rate, rodiv, rotrim);
402*b82e98bfSConor Dooley 
403*b82e98bfSConor Dooley 		delta = abs(nearest_rate - req->rate);
404*b82e98bfSConor Dooley 		if ((nearest_rate >= req->rate) && (delta < best_delta)) {
405*b82e98bfSConor Dooley 			best_parent_clk = parent_clk;
406*b82e98bfSConor Dooley 			best_parent_rate = parent_rate;
407*b82e98bfSConor Dooley 			best = nearest_rate;
408*b82e98bfSConor Dooley 			best_delta = delta;
409*b82e98bfSConor Dooley 
410*b82e98bfSConor Dooley 			if (delta == 0)
411*b82e98bfSConor Dooley 				break;
412*b82e98bfSConor Dooley 		}
413*b82e98bfSConor Dooley 	}
414*b82e98bfSConor Dooley 
415*b82e98bfSConor Dooley 	/* if no match found, retain old rate */
416*b82e98bfSConor Dooley 	if (!best_parent_clk) {
417*b82e98bfSConor Dooley 		pr_err("%s:%s, no parent found for rate %lu.\n",
418*b82e98bfSConor Dooley 		       __func__, clk_hw_get_name(hw), req->rate);
419*b82e98bfSConor Dooley 		return clk_hw_get_rate(hw);
420*b82e98bfSConor Dooley 	}
421*b82e98bfSConor Dooley 
422*b82e98bfSConor Dooley 	pr_debug("%s,rate %lu, best_parent(%s, %lu), best %lu, delta %d\n",
423*b82e98bfSConor Dooley 		 clk_hw_get_name(hw), req->rate,
424*b82e98bfSConor Dooley 		 clk_hw_get_name(best_parent_clk), best_parent_rate,
425*b82e98bfSConor Dooley 		 best, best_delta);
426*b82e98bfSConor Dooley 
427*b82e98bfSConor Dooley 	if (req->best_parent_rate)
428*b82e98bfSConor Dooley 		req->best_parent_rate = best_parent_rate;
429*b82e98bfSConor Dooley 
430*b82e98bfSConor Dooley 	if (req->best_parent_hw)
431*b82e98bfSConor Dooley 		req->best_parent_hw = best_parent_clk;
432*b82e98bfSConor Dooley 
433*b82e98bfSConor Dooley 	return best;
434*b82e98bfSConor Dooley }
435*b82e98bfSConor Dooley 
roclk_set_parent(struct clk_hw * hw,u8 index)436*b82e98bfSConor Dooley static int roclk_set_parent(struct clk_hw *hw, u8 index)
437*b82e98bfSConor Dooley {
438*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
439*b82e98bfSConor Dooley 	unsigned long flags;
440*b82e98bfSConor Dooley 	u32 v;
441*b82e98bfSConor Dooley 	int err;
442*b82e98bfSConor Dooley 
443*b82e98bfSConor Dooley 	if (refo->parent_map)
444*b82e98bfSConor Dooley 		index = refo->parent_map[index];
445*b82e98bfSConor Dooley 
446*b82e98bfSConor Dooley 	/* wait until ACTIVE bit is zero or timeout */
447*b82e98bfSConor Dooley 	err = readl_poll_timeout(refo->ctrl_reg, v, !(v & REFO_ACTIVE),
448*b82e98bfSConor Dooley 				 1, LOCK_TIMEOUT_US);
449*b82e98bfSConor Dooley 	if (err) {
450*b82e98bfSConor Dooley 		pr_err("%s: poll failed, clk active\n", clk_hw_get_name(hw));
451*b82e98bfSConor Dooley 		return err;
452*b82e98bfSConor Dooley 	}
453*b82e98bfSConor Dooley 
454*b82e98bfSConor Dooley 	spin_lock_irqsave(&refo->core->reg_lock, flags);
455*b82e98bfSConor Dooley 
456*b82e98bfSConor Dooley 	pic32_syskey_unlock();
457*b82e98bfSConor Dooley 
458*b82e98bfSConor Dooley 	/* calculate & apply new */
459*b82e98bfSConor Dooley 	v = readl(refo->ctrl_reg);
460*b82e98bfSConor Dooley 	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
461*b82e98bfSConor Dooley 	v |= index << REFO_SEL_SHIFT;
462*b82e98bfSConor Dooley 
463*b82e98bfSConor Dooley 	writel(v, refo->ctrl_reg);
464*b82e98bfSConor Dooley 
465*b82e98bfSConor Dooley 	spin_unlock_irqrestore(&refo->core->reg_lock, flags);
466*b82e98bfSConor Dooley 
467*b82e98bfSConor Dooley 	return 0;
468*b82e98bfSConor Dooley }
469*b82e98bfSConor Dooley 
roclk_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)470*b82e98bfSConor Dooley static int roclk_set_rate_and_parent(struct clk_hw *hw,
471*b82e98bfSConor Dooley 				     unsigned long rate,
472*b82e98bfSConor Dooley 				     unsigned long parent_rate,
473*b82e98bfSConor Dooley 				     u8 index)
474*b82e98bfSConor Dooley {
475*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
476*b82e98bfSConor Dooley 	unsigned long flags;
477*b82e98bfSConor Dooley 	u32 trim, rodiv, v;
478*b82e98bfSConor Dooley 	int err;
479*b82e98bfSConor Dooley 
480*b82e98bfSConor Dooley 	/* calculate new rodiv & rotrim for new rate */
481*b82e98bfSConor Dooley 	roclk_calc_div_trim(rate, parent_rate, &rodiv, &trim);
482*b82e98bfSConor Dooley 
483*b82e98bfSConor Dooley 	pr_debug("parent_rate = %lu, rate = %lu, div = %d, trim = %d\n",
484*b82e98bfSConor Dooley 		 parent_rate, rate, rodiv, trim);
485*b82e98bfSConor Dooley 
486*b82e98bfSConor Dooley 	/* wait till source change is active */
487*b82e98bfSConor Dooley 	err = readl_poll_timeout(refo->ctrl_reg, v,
488*b82e98bfSConor Dooley 				 !(v & (REFO_ACTIVE | REFO_DIVSW_EN)),
489*b82e98bfSConor Dooley 				 1, LOCK_TIMEOUT_US);
490*b82e98bfSConor Dooley 	if (err) {
491*b82e98bfSConor Dooley 		pr_err("%s: poll timedout, clock is still active\n", __func__);
492*b82e98bfSConor Dooley 		return err;
493*b82e98bfSConor Dooley 	}
494*b82e98bfSConor Dooley 
495*b82e98bfSConor Dooley 	spin_lock_irqsave(&refo->core->reg_lock, flags);
496*b82e98bfSConor Dooley 	v = readl(refo->ctrl_reg);
497*b82e98bfSConor Dooley 
498*b82e98bfSConor Dooley 	pic32_syskey_unlock();
499*b82e98bfSConor Dooley 
500*b82e98bfSConor Dooley 	/* apply parent, if required */
501*b82e98bfSConor Dooley 	if (refo->parent_map)
502*b82e98bfSConor Dooley 		index = refo->parent_map[index];
503*b82e98bfSConor Dooley 
504*b82e98bfSConor Dooley 	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
505*b82e98bfSConor Dooley 	v |= index << REFO_SEL_SHIFT;
506*b82e98bfSConor Dooley 
507*b82e98bfSConor Dooley 	/* apply RODIV */
508*b82e98bfSConor Dooley 	v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
509*b82e98bfSConor Dooley 	v |= rodiv << REFO_DIV_SHIFT;
510*b82e98bfSConor Dooley 	writel(v, refo->ctrl_reg);
511*b82e98bfSConor Dooley 
512*b82e98bfSConor Dooley 	/* apply ROTRIM */
513*b82e98bfSConor Dooley 	v = readl(refo->ctrl_reg + REFO_TRIM_REG);
514*b82e98bfSConor Dooley 	v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
515*b82e98bfSConor Dooley 	v |= trim << REFO_TRIM_SHIFT;
516*b82e98bfSConor Dooley 	writel(v, refo->ctrl_reg + REFO_TRIM_REG);
517*b82e98bfSConor Dooley 
518*b82e98bfSConor Dooley 	/* enable & activate divider switching */
519*b82e98bfSConor Dooley 	writel(REFO_ON | REFO_DIVSW_EN, PIC32_SET(refo->ctrl_reg));
520*b82e98bfSConor Dooley 
521*b82e98bfSConor Dooley 	/* wait till divswen is in-progress */
522*b82e98bfSConor Dooley 	err = readl_poll_timeout_atomic(refo->ctrl_reg, v, !(v & REFO_DIVSW_EN),
523*b82e98bfSConor Dooley 					1, LOCK_TIMEOUT_US);
524*b82e98bfSConor Dooley 	/* leave the clk gated as it was */
525*b82e98bfSConor Dooley 	writel(REFO_ON, PIC32_CLR(refo->ctrl_reg));
526*b82e98bfSConor Dooley 
527*b82e98bfSConor Dooley 	spin_unlock_irqrestore(&refo->core->reg_lock, flags);
528*b82e98bfSConor Dooley 
529*b82e98bfSConor Dooley 	return err;
530*b82e98bfSConor Dooley }
531*b82e98bfSConor Dooley 
roclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)532*b82e98bfSConor Dooley static int roclk_set_rate(struct clk_hw *hw, unsigned long rate,
533*b82e98bfSConor Dooley 			  unsigned long parent_rate)
534*b82e98bfSConor Dooley {
535*b82e98bfSConor Dooley 	u8 index = roclk_get_parent(hw);
536*b82e98bfSConor Dooley 
537*b82e98bfSConor Dooley 	return roclk_set_rate_and_parent(hw, rate, parent_rate, index);
538*b82e98bfSConor Dooley }
539*b82e98bfSConor Dooley 
540*b82e98bfSConor Dooley const struct clk_ops pic32_roclk_ops = {
541*b82e98bfSConor Dooley 	.enable			= roclk_enable,
542*b82e98bfSConor Dooley 	.disable		= roclk_disable,
543*b82e98bfSConor Dooley 	.is_enabled		= roclk_is_enabled,
544*b82e98bfSConor Dooley 	.get_parent		= roclk_get_parent,
545*b82e98bfSConor Dooley 	.set_parent		= roclk_set_parent,
546*b82e98bfSConor Dooley 	.determine_rate		= roclk_determine_rate,
547*b82e98bfSConor Dooley 	.recalc_rate		= roclk_recalc_rate,
548*b82e98bfSConor Dooley 	.set_rate_and_parent	= roclk_set_rate_and_parent,
549*b82e98bfSConor Dooley 	.set_rate		= roclk_set_rate,
550*b82e98bfSConor Dooley 	.init			= roclk_init,
551*b82e98bfSConor Dooley };
552*b82e98bfSConor Dooley 
pic32_refo_clk_register(const struct pic32_ref_osc_data * data,struct pic32_clk_common * core)553*b82e98bfSConor Dooley struct clk *pic32_refo_clk_register(const struct pic32_ref_osc_data *data,
554*b82e98bfSConor Dooley 				    struct pic32_clk_common *core)
555*b82e98bfSConor Dooley {
556*b82e98bfSConor Dooley 	struct pic32_ref_osc *refo;
557*b82e98bfSConor Dooley 	struct clk *clk;
558*b82e98bfSConor Dooley 
559*b82e98bfSConor Dooley 	refo = devm_kzalloc(core->dev, sizeof(*refo), GFP_KERNEL);
560*b82e98bfSConor Dooley 	if (!refo)
561*b82e98bfSConor Dooley 		return ERR_PTR(-ENOMEM);
562*b82e98bfSConor Dooley 
563*b82e98bfSConor Dooley 	refo->core = core;
564*b82e98bfSConor Dooley 	refo->hw.init = &data->init_data;
565*b82e98bfSConor Dooley 	refo->ctrl_reg = data->ctrl_reg + core->iobase;
566*b82e98bfSConor Dooley 	refo->parent_map = data->parent_map;
567*b82e98bfSConor Dooley 
568*b82e98bfSConor Dooley 	clk = devm_clk_register(core->dev, &refo->hw);
569*b82e98bfSConor Dooley 	if (IS_ERR(clk))
570*b82e98bfSConor Dooley 		dev_err(core->dev, "%s: clk_register() failed\n", __func__);
571*b82e98bfSConor Dooley 
572*b82e98bfSConor Dooley 	return clk;
573*b82e98bfSConor Dooley }
574*b82e98bfSConor Dooley 
575*b82e98bfSConor Dooley struct pic32_sys_pll {
576*b82e98bfSConor Dooley 	struct clk_hw hw;
577*b82e98bfSConor Dooley 	void __iomem *ctrl_reg;
578*b82e98bfSConor Dooley 	void __iomem *status_reg;
579*b82e98bfSConor Dooley 	u32 lock_mask;
580*b82e98bfSConor Dooley 	u32 idiv; /* PLL iclk divider, treated fixed */
581*b82e98bfSConor Dooley 	struct pic32_clk_common *core;
582*b82e98bfSConor Dooley };
583*b82e98bfSConor Dooley 
584*b82e98bfSConor Dooley #define clkhw_to_spll(_hw)	container_of(_hw, struct pic32_sys_pll, hw)
585*b82e98bfSConor Dooley 
spll_odiv_to_divider(u32 odiv)586*b82e98bfSConor Dooley static inline u32 spll_odiv_to_divider(u32 odiv)
587*b82e98bfSConor Dooley {
588*b82e98bfSConor Dooley 	odiv = clamp_val(odiv, PLL_ODIV_MIN, PLL_ODIV_MAX);
589*b82e98bfSConor Dooley 
590*b82e98bfSConor Dooley 	return 1 << odiv;
591*b82e98bfSConor Dooley }
592*b82e98bfSConor Dooley 
spll_calc_mult_div(struct pic32_sys_pll * pll,unsigned long rate,unsigned long parent_rate,u32 * mult_p,u32 * odiv_p)593*b82e98bfSConor Dooley static unsigned long spll_calc_mult_div(struct pic32_sys_pll *pll,
594*b82e98bfSConor Dooley 					unsigned long rate,
595*b82e98bfSConor Dooley 					unsigned long parent_rate,
596*b82e98bfSConor Dooley 					u32 *mult_p, u32 *odiv_p)
597*b82e98bfSConor Dooley {
598*b82e98bfSConor Dooley 	u32 mul, div, best_mul = 1, best_div = 1;
599*b82e98bfSConor Dooley 	unsigned long new_rate, best_rate = rate;
600*b82e98bfSConor Dooley 	unsigned int best_delta = -1, delta, match_found = 0;
601*b82e98bfSConor Dooley 	u64 rate64;
602*b82e98bfSConor Dooley 
603*b82e98bfSConor Dooley 	parent_rate /= pll->idiv;
604*b82e98bfSConor Dooley 
605*b82e98bfSConor Dooley 	for (mul = 1; mul <= PLL_MULT_MAX; mul++) {
606*b82e98bfSConor Dooley 		for (div = PLL_ODIV_MIN; div <= PLL_ODIV_MAX; div++) {
607*b82e98bfSConor Dooley 			rate64 = parent_rate;
608*b82e98bfSConor Dooley 			rate64 *= mul;
609*b82e98bfSConor Dooley 			do_div(rate64, 1 << div);
610*b82e98bfSConor Dooley 			new_rate = rate64;
611*b82e98bfSConor Dooley 			delta = abs(rate - new_rate);
612*b82e98bfSConor Dooley 			if ((new_rate >= rate) && (delta < best_delta)) {
613*b82e98bfSConor Dooley 				best_delta = delta;
614*b82e98bfSConor Dooley 				best_rate = new_rate;
615*b82e98bfSConor Dooley 				best_mul = mul;
616*b82e98bfSConor Dooley 				best_div = div;
617*b82e98bfSConor Dooley 				match_found = 1;
618*b82e98bfSConor Dooley 			}
619*b82e98bfSConor Dooley 		}
620*b82e98bfSConor Dooley 	}
621*b82e98bfSConor Dooley 
622*b82e98bfSConor Dooley 	if (!match_found) {
623*b82e98bfSConor Dooley 		pr_warn("spll: no match found\n");
624*b82e98bfSConor Dooley 		return 0;
625*b82e98bfSConor Dooley 	}
626*b82e98bfSConor Dooley 
627*b82e98bfSConor Dooley 	pr_debug("rate %lu, par_rate %lu/mult %u, div %u, best_rate %lu\n",
628*b82e98bfSConor Dooley 		 rate, parent_rate, best_mul, best_div, best_rate);
629*b82e98bfSConor Dooley 
630*b82e98bfSConor Dooley 	if (mult_p)
631*b82e98bfSConor Dooley 		*mult_p = best_mul - 1;
632*b82e98bfSConor Dooley 
633*b82e98bfSConor Dooley 	if (odiv_p)
634*b82e98bfSConor Dooley 		*odiv_p = best_div;
635*b82e98bfSConor Dooley 
636*b82e98bfSConor Dooley 	return best_rate;
637*b82e98bfSConor Dooley }
638*b82e98bfSConor Dooley 
spll_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)639*b82e98bfSConor Dooley static unsigned long spll_clk_recalc_rate(struct clk_hw *hw,
640*b82e98bfSConor Dooley 					  unsigned long parent_rate)
641*b82e98bfSConor Dooley {
642*b82e98bfSConor Dooley 	struct pic32_sys_pll *pll = clkhw_to_spll(hw);
643*b82e98bfSConor Dooley 	unsigned long pll_in_rate;
644*b82e98bfSConor Dooley 	u32 mult, odiv, div, v;
645*b82e98bfSConor Dooley 	u64 rate64;
646*b82e98bfSConor Dooley 
647*b82e98bfSConor Dooley 	v = readl(pll->ctrl_reg);
648*b82e98bfSConor Dooley 	odiv = ((v >> PLL_ODIV_SHIFT) & PLL_ODIV_MASK);
649*b82e98bfSConor Dooley 	mult = ((v >> PLL_MULT_SHIFT) & PLL_MULT_MASK) + 1;
650*b82e98bfSConor Dooley 	div = spll_odiv_to_divider(odiv);
651*b82e98bfSConor Dooley 
652*b82e98bfSConor Dooley 	/* pll_in_rate = parent_rate / idiv
653*b82e98bfSConor Dooley 	 * pll_out_rate = pll_in_rate * mult / div;
654*b82e98bfSConor Dooley 	 */
655*b82e98bfSConor Dooley 	pll_in_rate = parent_rate / pll->idiv;
656*b82e98bfSConor Dooley 	rate64 = pll_in_rate;
657*b82e98bfSConor Dooley 	rate64 *= mult;
658*b82e98bfSConor Dooley 	do_div(rate64, div);
659*b82e98bfSConor Dooley 
660*b82e98bfSConor Dooley 	return rate64;
661*b82e98bfSConor Dooley }
662*b82e98bfSConor Dooley 
spll_clk_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)663*b82e98bfSConor Dooley static int spll_clk_determine_rate(struct clk_hw *hw,
664*b82e98bfSConor Dooley 				   struct clk_rate_request *req)
665*b82e98bfSConor Dooley {
666*b82e98bfSConor Dooley 	struct pic32_sys_pll *pll = clkhw_to_spll(hw);
667*b82e98bfSConor Dooley 
668*b82e98bfSConor Dooley 	req->rate = spll_calc_mult_div(pll, req->rate, req->best_parent_rate,
669*b82e98bfSConor Dooley 				       NULL, NULL);
670*b82e98bfSConor Dooley 
671*b82e98bfSConor Dooley 	return 0;
672*b82e98bfSConor Dooley }
673*b82e98bfSConor Dooley 
spll_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)674*b82e98bfSConor Dooley static int spll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
675*b82e98bfSConor Dooley 			     unsigned long parent_rate)
676*b82e98bfSConor Dooley {
677*b82e98bfSConor Dooley 	struct pic32_sys_pll *pll = clkhw_to_spll(hw);
678*b82e98bfSConor Dooley 	unsigned long ret, flags;
679*b82e98bfSConor Dooley 	u32 mult, odiv, v;
680*b82e98bfSConor Dooley 	int err;
681*b82e98bfSConor Dooley 
682*b82e98bfSConor Dooley 	ret = spll_calc_mult_div(pll, rate, parent_rate, &mult, &odiv);
683*b82e98bfSConor Dooley 	if (!ret)
684*b82e98bfSConor Dooley 		return -EINVAL;
685*b82e98bfSConor Dooley 
686*b82e98bfSConor Dooley 	/*
687*b82e98bfSConor Dooley 	 * We can't change SPLL counters when it is in-active use
688*b82e98bfSConor Dooley 	 * by SYSCLK. So check before applying new counters/rate.
689*b82e98bfSConor Dooley 	 */
690*b82e98bfSConor Dooley 
691*b82e98bfSConor Dooley 	/* Is spll_clk active parent of sys_clk ? */
692*b82e98bfSConor Dooley 	if (unlikely(clk_hw_get_parent(pic32_sclk_hw) == hw)) {
693*b82e98bfSConor Dooley 		pr_err("%s: failed, clk in-use\n", __func__);
694*b82e98bfSConor Dooley 		return -EBUSY;
695*b82e98bfSConor Dooley 	}
696*b82e98bfSConor Dooley 
697*b82e98bfSConor Dooley 	spin_lock_irqsave(&pll->core->reg_lock, flags);
698*b82e98bfSConor Dooley 
699*b82e98bfSConor Dooley 	/* apply new multiplier & divisor */
700*b82e98bfSConor Dooley 	v = readl(pll->ctrl_reg);
701*b82e98bfSConor Dooley 	v &= ~(PLL_MULT_MASK << PLL_MULT_SHIFT);
702*b82e98bfSConor Dooley 	v &= ~(PLL_ODIV_MASK << PLL_ODIV_SHIFT);
703*b82e98bfSConor Dooley 	v |= (mult << PLL_MULT_SHIFT) | (odiv << PLL_ODIV_SHIFT);
704*b82e98bfSConor Dooley 
705*b82e98bfSConor Dooley 	/* sys unlock before write */
706*b82e98bfSConor Dooley 	pic32_syskey_unlock();
707*b82e98bfSConor Dooley 
708*b82e98bfSConor Dooley 	writel(v, pll->ctrl_reg);
709*b82e98bfSConor Dooley 	cpu_relax();
710*b82e98bfSConor Dooley 
711*b82e98bfSConor Dooley 	/* insert few nops (5-stage) to ensure CPU does not hang */
712*b82e98bfSConor Dooley 	cpu_nop5();
713*b82e98bfSConor Dooley 	cpu_nop5();
714*b82e98bfSConor Dooley 
715*b82e98bfSConor Dooley 	/* Wait until PLL is locked (maximum 100 usecs). */
716*b82e98bfSConor Dooley 	err = readl_poll_timeout_atomic(pll->status_reg, v,
717*b82e98bfSConor Dooley 					v & pll->lock_mask, 1, 100);
718*b82e98bfSConor Dooley 	spin_unlock_irqrestore(&pll->core->reg_lock, flags);
719*b82e98bfSConor Dooley 
720*b82e98bfSConor Dooley 	return err;
721*b82e98bfSConor Dooley }
722*b82e98bfSConor Dooley 
723*b82e98bfSConor Dooley /* SPLL clock operation */
724*b82e98bfSConor Dooley const struct clk_ops pic32_spll_ops = {
725*b82e98bfSConor Dooley 	.recalc_rate	= spll_clk_recalc_rate,
726*b82e98bfSConor Dooley 	.determine_rate = spll_clk_determine_rate,
727*b82e98bfSConor Dooley 	.set_rate	= spll_clk_set_rate,
728*b82e98bfSConor Dooley };
729*b82e98bfSConor Dooley 
pic32_spll_clk_register(const struct pic32_sys_pll_data * data,struct pic32_clk_common * core)730*b82e98bfSConor Dooley struct clk *pic32_spll_clk_register(const struct pic32_sys_pll_data *data,
731*b82e98bfSConor Dooley 				    struct pic32_clk_common *core)
732*b82e98bfSConor Dooley {
733*b82e98bfSConor Dooley 	struct pic32_sys_pll *spll;
734*b82e98bfSConor Dooley 	struct clk *clk;
735*b82e98bfSConor Dooley 
736*b82e98bfSConor Dooley 	spll = devm_kzalloc(core->dev, sizeof(*spll), GFP_KERNEL);
737*b82e98bfSConor Dooley 	if (!spll)
738*b82e98bfSConor Dooley 		return ERR_PTR(-ENOMEM);
739*b82e98bfSConor Dooley 
740*b82e98bfSConor Dooley 	spll->core = core;
741*b82e98bfSConor Dooley 	spll->hw.init = &data->init_data;
742*b82e98bfSConor Dooley 	spll->ctrl_reg = data->ctrl_reg + core->iobase;
743*b82e98bfSConor Dooley 	spll->status_reg = data->status_reg + core->iobase;
744*b82e98bfSConor Dooley 	spll->lock_mask = data->lock_mask;
745*b82e98bfSConor Dooley 
746*b82e98bfSConor Dooley 	/* cache PLL idiv; PLL driver uses it as constant.*/
747*b82e98bfSConor Dooley 	spll->idiv = (readl(spll->ctrl_reg) >> PLL_IDIV_SHIFT) & PLL_IDIV_MASK;
748*b82e98bfSConor Dooley 	spll->idiv += 1;
749*b82e98bfSConor Dooley 
750*b82e98bfSConor Dooley 	clk = devm_clk_register(core->dev, &spll->hw);
751*b82e98bfSConor Dooley 	if (IS_ERR(clk))
752*b82e98bfSConor Dooley 		dev_err(core->dev, "sys_pll: clk_register() failed\n");
753*b82e98bfSConor Dooley 
754*b82e98bfSConor Dooley 	return clk;
755*b82e98bfSConor Dooley }
756*b82e98bfSConor Dooley 
757*b82e98bfSConor Dooley /* System mux clock(aka SCLK) */
758*b82e98bfSConor Dooley 
759*b82e98bfSConor Dooley struct pic32_sys_clk {
760*b82e98bfSConor Dooley 	struct clk_hw hw;
761*b82e98bfSConor Dooley 	void __iomem *mux_reg;
762*b82e98bfSConor Dooley 	void __iomem *slew_reg;
763*b82e98bfSConor Dooley 	u32 slew_div;
764*b82e98bfSConor Dooley 	const u32 *parent_map;
765*b82e98bfSConor Dooley 	struct pic32_clk_common *core;
766*b82e98bfSConor Dooley };
767*b82e98bfSConor Dooley 
768*b82e98bfSConor Dooley #define clkhw_to_sys_clk(_hw)	container_of(_hw, struct pic32_sys_clk, hw)
769*b82e98bfSConor Dooley 
sclk_get_rate(struct clk_hw * hw,unsigned long parent_rate)770*b82e98bfSConor Dooley static unsigned long sclk_get_rate(struct clk_hw *hw, unsigned long parent_rate)
771*b82e98bfSConor Dooley {
772*b82e98bfSConor Dooley 	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
773*b82e98bfSConor Dooley 	u32 div;
774*b82e98bfSConor Dooley 
775*b82e98bfSConor Dooley 	div = (readl(sclk->slew_reg) >> SLEW_SYSDIV_SHIFT) & SLEW_SYSDIV;
776*b82e98bfSConor Dooley 	div += 1; /* sys-div to divider */
777*b82e98bfSConor Dooley 
778*b82e98bfSConor Dooley 	return parent_rate / div;
779*b82e98bfSConor Dooley }
780*b82e98bfSConor Dooley 
sclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)781*b82e98bfSConor Dooley static int sclk_set_rate(struct clk_hw *hw,
782*b82e98bfSConor Dooley 			 unsigned long rate, unsigned long parent_rate)
783*b82e98bfSConor Dooley {
784*b82e98bfSConor Dooley 	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
785*b82e98bfSConor Dooley 	unsigned long flags;
786*b82e98bfSConor Dooley 	u32 v, div;
787*b82e98bfSConor Dooley 	int err;
788*b82e98bfSConor Dooley 
789*b82e98bfSConor Dooley 	div = parent_rate / rate;
790*b82e98bfSConor Dooley 
791*b82e98bfSConor Dooley 	spin_lock_irqsave(&sclk->core->reg_lock, flags);
792*b82e98bfSConor Dooley 
793*b82e98bfSConor Dooley 	/* apply new div */
794*b82e98bfSConor Dooley 	v = readl(sclk->slew_reg);
795*b82e98bfSConor Dooley 	v &= ~(SLEW_SYSDIV << SLEW_SYSDIV_SHIFT);
796*b82e98bfSConor Dooley 	v |= (div - 1) << SLEW_SYSDIV_SHIFT;
797*b82e98bfSConor Dooley 
798*b82e98bfSConor Dooley 	pic32_syskey_unlock();
799*b82e98bfSConor Dooley 
800*b82e98bfSConor Dooley 	writel(v, sclk->slew_reg);
801*b82e98bfSConor Dooley 
802*b82e98bfSConor Dooley 	/* wait until BUSY is cleared */
803*b82e98bfSConor Dooley 	err = readl_poll_timeout_atomic(sclk->slew_reg, v,
804*b82e98bfSConor Dooley 					!(v & SLEW_BUSY), 1, LOCK_TIMEOUT_US);
805*b82e98bfSConor Dooley 
806*b82e98bfSConor Dooley 	spin_unlock_irqrestore(&sclk->core->reg_lock, flags);
807*b82e98bfSConor Dooley 
808*b82e98bfSConor Dooley 	return err;
809*b82e98bfSConor Dooley }
810*b82e98bfSConor Dooley 
sclk_get_parent(struct clk_hw * hw)811*b82e98bfSConor Dooley static u8 sclk_get_parent(struct clk_hw *hw)
812*b82e98bfSConor Dooley {
813*b82e98bfSConor Dooley 	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
814*b82e98bfSConor Dooley 	u32 i, v;
815*b82e98bfSConor Dooley 
816*b82e98bfSConor Dooley 	v = (readl(sclk->mux_reg) >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
817*b82e98bfSConor Dooley 
818*b82e98bfSConor Dooley 	if (sclk->parent_map) {
819*b82e98bfSConor Dooley 		for (i = 0; i < clk_hw_get_num_parents(hw); i++)
820*b82e98bfSConor Dooley 			if (sclk->parent_map[i] == v)
821*b82e98bfSConor Dooley 				return i;
822*b82e98bfSConor Dooley 	}
823*b82e98bfSConor Dooley 
824*b82e98bfSConor Dooley 	return v;
825*b82e98bfSConor Dooley }
826*b82e98bfSConor Dooley 
sclk_set_parent(struct clk_hw * hw,u8 index)827*b82e98bfSConor Dooley static int sclk_set_parent(struct clk_hw *hw, u8 index)
828*b82e98bfSConor Dooley {
829*b82e98bfSConor Dooley 	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
830*b82e98bfSConor Dooley 	unsigned long flags;
831*b82e98bfSConor Dooley 	u32 nosc, cosc, v;
832*b82e98bfSConor Dooley 	int err;
833*b82e98bfSConor Dooley 
834*b82e98bfSConor Dooley 	spin_lock_irqsave(&sclk->core->reg_lock, flags);
835*b82e98bfSConor Dooley 
836*b82e98bfSConor Dooley 	/* find new_osc */
837*b82e98bfSConor Dooley 	nosc = sclk->parent_map ? sclk->parent_map[index] : index;
838*b82e98bfSConor Dooley 
839*b82e98bfSConor Dooley 	/* set new parent */
840*b82e98bfSConor Dooley 	v = readl(sclk->mux_reg);
841*b82e98bfSConor Dooley 	v &= ~(OSC_NEW_MASK << OSC_NEW_SHIFT);
842*b82e98bfSConor Dooley 	v |= nosc << OSC_NEW_SHIFT;
843*b82e98bfSConor Dooley 
844*b82e98bfSConor Dooley 	pic32_syskey_unlock();
845*b82e98bfSConor Dooley 
846*b82e98bfSConor Dooley 	writel(v, sclk->mux_reg);
847*b82e98bfSConor Dooley 
848*b82e98bfSConor Dooley 	/* initate switch */
849*b82e98bfSConor Dooley 	writel(OSC_SWEN, PIC32_SET(sclk->mux_reg));
850*b82e98bfSConor Dooley 	cpu_relax();
851*b82e98bfSConor Dooley 
852*b82e98bfSConor Dooley 	/* add nop to flush pipeline (as cpu_clk is in-flux) */
853*b82e98bfSConor Dooley 	cpu_nop5();
854*b82e98bfSConor Dooley 
855*b82e98bfSConor Dooley 	/* wait for SWEN bit to clear */
856*b82e98bfSConor Dooley 	err = readl_poll_timeout_atomic(sclk->slew_reg, v,
857*b82e98bfSConor Dooley 					!(v & OSC_SWEN), 1, LOCK_TIMEOUT_US);
858*b82e98bfSConor Dooley 
859*b82e98bfSConor Dooley 	spin_unlock_irqrestore(&sclk->core->reg_lock, flags);
860*b82e98bfSConor Dooley 
861*b82e98bfSConor Dooley 	/*
862*b82e98bfSConor Dooley 	 * SCLK clock-switching logic might reject a clock switching request
863*b82e98bfSConor Dooley 	 * if pre-requisites (like new clk_src not present or unstable) are
864*b82e98bfSConor Dooley 	 * not met.
865*b82e98bfSConor Dooley 	 * So confirm before claiming success.
866*b82e98bfSConor Dooley 	 */
867*b82e98bfSConor Dooley 	cosc = (readl(sclk->mux_reg) >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
868*b82e98bfSConor Dooley 	if (cosc != nosc) {
869*b82e98bfSConor Dooley 		pr_err("%s: err, failed to set_parent() to %d, current %d\n",
870*b82e98bfSConor Dooley 		       clk_hw_get_name(hw), nosc, cosc);
871*b82e98bfSConor Dooley 		err = -EBUSY;
872*b82e98bfSConor Dooley 	}
873*b82e98bfSConor Dooley 
874*b82e98bfSConor Dooley 	return err;
875*b82e98bfSConor Dooley }
876*b82e98bfSConor Dooley 
sclk_init(struct clk_hw * hw)877*b82e98bfSConor Dooley static int sclk_init(struct clk_hw *hw)
878*b82e98bfSConor Dooley {
879*b82e98bfSConor Dooley 	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
880*b82e98bfSConor Dooley 	unsigned long flags;
881*b82e98bfSConor Dooley 	u32 v;
882*b82e98bfSConor Dooley 
883*b82e98bfSConor Dooley 	/* Maintain reference to this clk, required in spll_clk_set_rate() */
884*b82e98bfSConor Dooley 	pic32_sclk_hw = hw;
885*b82e98bfSConor Dooley 
886*b82e98bfSConor Dooley 	/* apply slew divider on both up and down scaling */
887*b82e98bfSConor Dooley 	if (sclk->slew_div) {
888*b82e98bfSConor Dooley 		spin_lock_irqsave(&sclk->core->reg_lock, flags);
889*b82e98bfSConor Dooley 		v = readl(sclk->slew_reg);
890*b82e98bfSConor Dooley 		v &= ~(SLEW_DIV << SLEW_DIV_SHIFT);
891*b82e98bfSConor Dooley 		v |= sclk->slew_div << SLEW_DIV_SHIFT;
892*b82e98bfSConor Dooley 		v |= SLEW_DOWNEN | SLEW_UPEN;
893*b82e98bfSConor Dooley 		writel(v, sclk->slew_reg);
894*b82e98bfSConor Dooley 		spin_unlock_irqrestore(&sclk->core->reg_lock, flags);
895*b82e98bfSConor Dooley 	}
896*b82e98bfSConor Dooley 
897*b82e98bfSConor Dooley 	return 0;
898*b82e98bfSConor Dooley }
899*b82e98bfSConor Dooley 
900*b82e98bfSConor Dooley /* sclk with post-divider */
901*b82e98bfSConor Dooley const struct clk_ops pic32_sclk_ops = {
902*b82e98bfSConor Dooley 	.get_parent	= sclk_get_parent,
903*b82e98bfSConor Dooley 	.set_parent	= sclk_set_parent,
904*b82e98bfSConor Dooley 	.set_rate	= sclk_set_rate,
905*b82e98bfSConor Dooley 	.recalc_rate	= sclk_get_rate,
906*b82e98bfSConor Dooley 	.init		= sclk_init,
907*b82e98bfSConor Dooley 	.determine_rate = __clk_mux_determine_rate,
908*b82e98bfSConor Dooley };
909*b82e98bfSConor Dooley 
910*b82e98bfSConor Dooley /* sclk with no slew and no post-divider */
911*b82e98bfSConor Dooley const struct clk_ops pic32_sclk_no_div_ops = {
912*b82e98bfSConor Dooley 	.get_parent	= sclk_get_parent,
913*b82e98bfSConor Dooley 	.set_parent	= sclk_set_parent,
914*b82e98bfSConor Dooley 	.init		= sclk_init,
915*b82e98bfSConor Dooley 	.determine_rate = __clk_mux_determine_rate,
916*b82e98bfSConor Dooley };
917*b82e98bfSConor Dooley 
pic32_sys_clk_register(const struct pic32_sys_clk_data * data,struct pic32_clk_common * core)918*b82e98bfSConor Dooley struct clk *pic32_sys_clk_register(const struct pic32_sys_clk_data *data,
919*b82e98bfSConor Dooley 				   struct pic32_clk_common *core)
920*b82e98bfSConor Dooley {
921*b82e98bfSConor Dooley 	struct pic32_sys_clk *sclk;
922*b82e98bfSConor Dooley 	struct clk *clk;
923*b82e98bfSConor Dooley 
924*b82e98bfSConor Dooley 	sclk = devm_kzalloc(core->dev, sizeof(*sclk), GFP_KERNEL);
925*b82e98bfSConor Dooley 	if (!sclk)
926*b82e98bfSConor Dooley 		return ERR_PTR(-ENOMEM);
927*b82e98bfSConor Dooley 
928*b82e98bfSConor Dooley 	sclk->core = core;
929*b82e98bfSConor Dooley 	sclk->hw.init = &data->init_data;
930*b82e98bfSConor Dooley 	sclk->mux_reg = data->mux_reg + core->iobase;
931*b82e98bfSConor Dooley 	sclk->slew_reg = data->slew_reg + core->iobase;
932*b82e98bfSConor Dooley 	sclk->slew_div = data->slew_div;
933*b82e98bfSConor Dooley 	sclk->parent_map = data->parent_map;
934*b82e98bfSConor Dooley 
935*b82e98bfSConor Dooley 	clk = devm_clk_register(core->dev, &sclk->hw);
936*b82e98bfSConor Dooley 	if (IS_ERR(clk))
937*b82e98bfSConor Dooley 		dev_err(core->dev, "%s: clk register failed\n", __func__);
938*b82e98bfSConor Dooley 
939*b82e98bfSConor Dooley 	return clk;
940*b82e98bfSConor Dooley }
941*b82e98bfSConor Dooley 
942*b82e98bfSConor Dooley /* secondary oscillator */
943*b82e98bfSConor Dooley struct pic32_sec_osc {
944*b82e98bfSConor Dooley 	struct clk_hw hw;
945*b82e98bfSConor Dooley 	void __iomem *enable_reg;
946*b82e98bfSConor Dooley 	void __iomem *status_reg;
947*b82e98bfSConor Dooley 	u32 enable_mask;
948*b82e98bfSConor Dooley 	u32 status_mask;
949*b82e98bfSConor Dooley 	unsigned long fixed_rate;
950*b82e98bfSConor Dooley 	struct pic32_clk_common *core;
951*b82e98bfSConor Dooley };
952*b82e98bfSConor Dooley 
953*b82e98bfSConor Dooley #define clkhw_to_sosc(_hw)	container_of(_hw, struct pic32_sec_osc, hw)
sosc_clk_enable(struct clk_hw * hw)954*b82e98bfSConor Dooley static int sosc_clk_enable(struct clk_hw *hw)
955*b82e98bfSConor Dooley {
956*b82e98bfSConor Dooley 	struct pic32_sec_osc *sosc = clkhw_to_sosc(hw);
957*b82e98bfSConor Dooley 	u32 v;
958*b82e98bfSConor Dooley 
959*b82e98bfSConor Dooley 	/* enable SOSC */
960*b82e98bfSConor Dooley 	pic32_syskey_unlock();
961*b82e98bfSConor Dooley 	writel(sosc->enable_mask, PIC32_SET(sosc->enable_reg));
962*b82e98bfSConor Dooley 
963*b82e98bfSConor Dooley 	/* wait till warm-up period expires or ready-status is updated */
964*b82e98bfSConor Dooley 	return readl_poll_timeout_atomic(sosc->status_reg, v,
965*b82e98bfSConor Dooley 					 v & sosc->status_mask, 1, 100);
966*b82e98bfSConor Dooley }
967*b82e98bfSConor Dooley 
sosc_clk_disable(struct clk_hw * hw)968*b82e98bfSConor Dooley static void sosc_clk_disable(struct clk_hw *hw)
969*b82e98bfSConor Dooley {
970*b82e98bfSConor Dooley 	struct pic32_sec_osc *sosc = clkhw_to_sosc(hw);
971*b82e98bfSConor Dooley 
972*b82e98bfSConor Dooley 	pic32_syskey_unlock();
973*b82e98bfSConor Dooley 	writel(sosc->enable_mask, PIC32_CLR(sosc->enable_reg));
974*b82e98bfSConor Dooley }
975*b82e98bfSConor Dooley 
sosc_clk_is_enabled(struct clk_hw * hw)976*b82e98bfSConor Dooley static int sosc_clk_is_enabled(struct clk_hw *hw)
977*b82e98bfSConor Dooley {
978*b82e98bfSConor Dooley 	struct pic32_sec_osc *sosc = clkhw_to_sosc(hw);
979*b82e98bfSConor Dooley 	u32 enabled, ready;
980*b82e98bfSConor Dooley 
981*b82e98bfSConor Dooley 	/* check enabled and ready status */
982*b82e98bfSConor Dooley 	enabled = readl(sosc->enable_reg) & sosc->enable_mask;
983*b82e98bfSConor Dooley 	ready = readl(sosc->status_reg) & sosc->status_mask;
984*b82e98bfSConor Dooley 
985*b82e98bfSConor Dooley 	return enabled && ready;
986*b82e98bfSConor Dooley }
987*b82e98bfSConor Dooley 
sosc_clk_calc_rate(struct clk_hw * hw,unsigned long parent_rate)988*b82e98bfSConor Dooley static unsigned long sosc_clk_calc_rate(struct clk_hw *hw,
989*b82e98bfSConor Dooley 					unsigned long parent_rate)
990*b82e98bfSConor Dooley {
991*b82e98bfSConor Dooley 	return clkhw_to_sosc(hw)->fixed_rate;
992*b82e98bfSConor Dooley }
993*b82e98bfSConor Dooley 
994*b82e98bfSConor Dooley const struct clk_ops pic32_sosc_ops = {
995*b82e98bfSConor Dooley 	.enable = sosc_clk_enable,
996*b82e98bfSConor Dooley 	.disable = sosc_clk_disable,
997*b82e98bfSConor Dooley 	.is_enabled = sosc_clk_is_enabled,
998*b82e98bfSConor Dooley 	.recalc_rate = sosc_clk_calc_rate,
999*b82e98bfSConor Dooley };
1000*b82e98bfSConor Dooley 
pic32_sosc_clk_register(const struct pic32_sec_osc_data * data,struct pic32_clk_common * core)1001*b82e98bfSConor Dooley struct clk *pic32_sosc_clk_register(const struct pic32_sec_osc_data *data,
1002*b82e98bfSConor Dooley 				    struct pic32_clk_common *core)
1003*b82e98bfSConor Dooley {
1004*b82e98bfSConor Dooley 	struct pic32_sec_osc *sosc;
1005*b82e98bfSConor Dooley 
1006*b82e98bfSConor Dooley 	sosc = devm_kzalloc(core->dev, sizeof(*sosc), GFP_KERNEL);
1007*b82e98bfSConor Dooley 	if (!sosc)
1008*b82e98bfSConor Dooley 		return ERR_PTR(-ENOMEM);
1009*b82e98bfSConor Dooley 
1010*b82e98bfSConor Dooley 	sosc->core = core;
1011*b82e98bfSConor Dooley 	sosc->hw.init = &data->init_data;
1012*b82e98bfSConor Dooley 	sosc->fixed_rate = data->fixed_rate;
1013*b82e98bfSConor Dooley 	sosc->enable_mask = data->enable_mask;
1014*b82e98bfSConor Dooley 	sosc->status_mask = data->status_mask;
1015*b82e98bfSConor Dooley 	sosc->enable_reg = data->enable_reg + core->iobase;
1016*b82e98bfSConor Dooley 	sosc->status_reg = data->status_reg + core->iobase;
1017*b82e98bfSConor Dooley 
1018*b82e98bfSConor Dooley 	return devm_clk_register(core->dev, &sosc->hw);
1019*b82e98bfSConor Dooley }
1020