1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (C) 2014 Broadcom Corporation
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or
5*4882a593Smuzhiyun * modify it under the terms of the GNU General Public License as
6*4882a593Smuzhiyun * published by the Free Software Foundation version 2.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9*4882a593Smuzhiyun * kind, whether express or implied; without even the implied warranty
10*4882a593Smuzhiyun * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11*4882a593Smuzhiyun * GNU General Public License for more details.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/err.h>
17*4882a593Smuzhiyun #include <linux/clk-provider.h>
18*4882a593Smuzhiyun #include <linux/io.h>
19*4882a593Smuzhiyun #include <linux/of.h>
20*4882a593Smuzhiyun #include <linux/clkdev.h>
21*4882a593Smuzhiyun #include <linux/of_address.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include "clk-iproc.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define IPROC_CLK_MAX_FREQ_POLICY 0x3
26*4882a593Smuzhiyun #define IPROC_CLK_POLICY_FREQ_OFFSET 0x008
27*4882a593Smuzhiyun #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8
28*4882a593Smuzhiyun #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define IPROC_CLK_PLLARMA_OFFSET 0xc00
31*4882a593Smuzhiyun #define IPROC_CLK_PLLARMA_LOCK_SHIFT 28
32*4882a593Smuzhiyun #define IPROC_CLK_PLLARMA_PDIV_SHIFT 24
33*4882a593Smuzhiyun #define IPROC_CLK_PLLARMA_PDIV_MASK 0xf
34*4882a593Smuzhiyun #define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8
35*4882a593Smuzhiyun #define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun #define IPROC_CLK_PLLARMB_OFFSET 0xc04
38*4882a593Smuzhiyun #define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #define IPROC_CLK_PLLARMC_OFFSET 0xc08
41*4882a593Smuzhiyun #define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8
42*4882a593Smuzhiyun #define IPROC_CLK_PLLARMC_MDIV_MASK 0xff
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20
45*4882a593Smuzhiyun #define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24
48*4882a593Smuzhiyun #define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29
49*4882a593Smuzhiyun #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20
50*4882a593Smuzhiyun #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff
51*4882a593Smuzhiyun #define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun #define IPROC_CLK_ARM_DIV_OFFSET 0xe00
54*4882a593Smuzhiyun #define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4
55*4882a593Smuzhiyun #define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun #define IPROC_CLK_POLICY_DBG_OFFSET 0xec0
58*4882a593Smuzhiyun #define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12
59*4882a593Smuzhiyun #define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun enum iproc_arm_pll_fid {
62*4882a593Smuzhiyun ARM_PLL_FID_CRYSTAL_CLK = 0,
63*4882a593Smuzhiyun ARM_PLL_FID_SYS_CLK = 2,
64*4882a593Smuzhiyun ARM_PLL_FID_CH0_SLOW_CLK = 6,
65*4882a593Smuzhiyun ARM_PLL_FID_CH1_FAST_CLK = 7
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun struct iproc_arm_pll {
69*4882a593Smuzhiyun struct clk_hw hw;
70*4882a593Smuzhiyun void __iomem *base;
71*4882a593Smuzhiyun unsigned long rate;
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun #define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
75*4882a593Smuzhiyun
__get_fid(struct iproc_arm_pll * pll)76*4882a593Smuzhiyun static unsigned int __get_fid(struct iproc_arm_pll *pll)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun u32 val;
79*4882a593Smuzhiyun unsigned int policy, fid, active_fid;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET);
82*4882a593Smuzhiyun if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT))
83*4882a593Smuzhiyun policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK;
84*4882a593Smuzhiyun else
85*4882a593Smuzhiyun policy = 0;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* something is seriously wrong */
88*4882a593Smuzhiyun BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET);
91*4882a593Smuzhiyun fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) &
92*4882a593Smuzhiyun IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET);
95*4882a593Smuzhiyun active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK &
96*4882a593Smuzhiyun (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT);
97*4882a593Smuzhiyun if (fid != active_fid) {
98*4882a593Smuzhiyun pr_debug("%s: fid override %u->%u\n", __func__, fid,
99*4882a593Smuzhiyun active_fid);
100*4882a593Smuzhiyun fid = active_fid;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun pr_debug("%s: active fid: %u\n", __func__, fid);
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun return fid;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /*
109*4882a593Smuzhiyun * Determine the mdiv (post divider) based on the frequency ID being used.
110*4882a593Smuzhiyun * There are 4 sources that can be used to derive the output clock rate:
111*4882a593Smuzhiyun * - 25 MHz Crystal
112*4882a593Smuzhiyun * - System clock
113*4882a593Smuzhiyun * - PLL channel 0 (slow clock)
114*4882a593Smuzhiyun * - PLL channel 1 (fast clock)
115*4882a593Smuzhiyun */
__get_mdiv(struct iproc_arm_pll * pll)116*4882a593Smuzhiyun static int __get_mdiv(struct iproc_arm_pll *pll)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun unsigned int fid;
119*4882a593Smuzhiyun int mdiv;
120*4882a593Smuzhiyun u32 val;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun fid = __get_fid(pll);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun switch (fid) {
125*4882a593Smuzhiyun case ARM_PLL_FID_CRYSTAL_CLK:
126*4882a593Smuzhiyun case ARM_PLL_FID_SYS_CLK:
127*4882a593Smuzhiyun mdiv = 1;
128*4882a593Smuzhiyun break;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun case ARM_PLL_FID_CH0_SLOW_CLK:
131*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
132*4882a593Smuzhiyun mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK;
133*4882a593Smuzhiyun if (mdiv == 0)
134*4882a593Smuzhiyun mdiv = 256;
135*4882a593Smuzhiyun break;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun case ARM_PLL_FID_CH1_FAST_CLK:
138*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET);
139*4882a593Smuzhiyun mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK;
140*4882a593Smuzhiyun if (mdiv == 0)
141*4882a593Smuzhiyun mdiv = 256;
142*4882a593Smuzhiyun break;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun default:
145*4882a593Smuzhiyun mdiv = -EFAULT;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return mdiv;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
__get_ndiv(struct iproc_arm_pll * pll)151*4882a593Smuzhiyun static unsigned int __get_ndiv(struct iproc_arm_pll *pll)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun u32 val;
154*4882a593Smuzhiyun unsigned int ndiv_int, ndiv_frac, ndiv;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET);
157*4882a593Smuzhiyun if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) {
158*4882a593Smuzhiyun /*
159*4882a593Smuzhiyun * offset mode is active. Read the ndiv from the PLLARM OFFSET
160*4882a593Smuzhiyun * register
161*4882a593Smuzhiyun */
162*4882a593Smuzhiyun ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) &
163*4882a593Smuzhiyun IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK;
164*4882a593Smuzhiyun if (ndiv_int == 0)
165*4882a593Smuzhiyun ndiv_int = 256;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK;
168*4882a593Smuzhiyun } else {
169*4882a593Smuzhiyun /* offset mode not active */
170*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
171*4882a593Smuzhiyun ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) &
172*4882a593Smuzhiyun IPROC_CLK_PLLARMA_NDIV_INT_MASK;
173*4882a593Smuzhiyun if (ndiv_int == 0)
174*4882a593Smuzhiyun ndiv_int = 1024;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET);
177*4882a593Smuzhiyun ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun ndiv = (ndiv_int << 20) | ndiv_frac;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun return ndiv;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun /*
186*4882a593Smuzhiyun * The output frequency of the ARM PLL is calculated based on the ARM PLL
187*4882a593Smuzhiyun * divider values:
188*4882a593Smuzhiyun * pdiv = ARM PLL pre-divider
189*4882a593Smuzhiyun * ndiv = ARM PLL multiplier
190*4882a593Smuzhiyun * mdiv = ARM PLL post divider
191*4882a593Smuzhiyun *
192*4882a593Smuzhiyun * The frequency is calculated by:
193*4882a593Smuzhiyun * ((ndiv * parent clock rate) / pdiv) / mdiv
194*4882a593Smuzhiyun */
iproc_arm_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)195*4882a593Smuzhiyun static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw,
196*4882a593Smuzhiyun unsigned long parent_rate)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun struct iproc_arm_pll *pll = to_iproc_arm_pll(hw);
199*4882a593Smuzhiyun u32 val;
200*4882a593Smuzhiyun int mdiv;
201*4882a593Smuzhiyun u64 ndiv;
202*4882a593Smuzhiyun unsigned int pdiv;
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun /* in bypass mode, use parent rate */
205*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
206*4882a593Smuzhiyun if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) {
207*4882a593Smuzhiyun pll->rate = parent_rate;
208*4882a593Smuzhiyun return pll->rate;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun /* PLL needs to be locked */
212*4882a593Smuzhiyun val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
213*4882a593Smuzhiyun if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) {
214*4882a593Smuzhiyun pll->rate = 0;
215*4882a593Smuzhiyun return 0;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) &
219*4882a593Smuzhiyun IPROC_CLK_PLLARMA_PDIV_MASK;
220*4882a593Smuzhiyun if (pdiv == 0)
221*4882a593Smuzhiyun pdiv = 16;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun ndiv = __get_ndiv(pll);
224*4882a593Smuzhiyun mdiv = __get_mdiv(pll);
225*4882a593Smuzhiyun if (mdiv <= 0) {
226*4882a593Smuzhiyun pll->rate = 0;
227*4882a593Smuzhiyun return 0;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun pll->rate = (ndiv * parent_rate) >> 20;
230*4882a593Smuzhiyun pll->rate = (pll->rate / pdiv) / mdiv;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__,
233*4882a593Smuzhiyun pll->rate, parent_rate);
234*4882a593Smuzhiyun pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__,
235*4882a593Smuzhiyun (unsigned int)(ndiv >> 20), pdiv, mdiv);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun return pll->rate;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun static const struct clk_ops iproc_arm_pll_ops = {
241*4882a593Smuzhiyun .recalc_rate = iproc_arm_pll_recalc_rate,
242*4882a593Smuzhiyun };
243*4882a593Smuzhiyun
iproc_armpll_setup(struct device_node * node)244*4882a593Smuzhiyun void __init iproc_armpll_setup(struct device_node *node)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun int ret;
247*4882a593Smuzhiyun struct iproc_arm_pll *pll;
248*4882a593Smuzhiyun struct clk_init_data init;
249*4882a593Smuzhiyun const char *parent_name;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun pll = kzalloc(sizeof(*pll), GFP_KERNEL);
252*4882a593Smuzhiyun if (WARN_ON(!pll))
253*4882a593Smuzhiyun return;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun pll->base = of_iomap(node, 0);
256*4882a593Smuzhiyun if (WARN_ON(!pll->base))
257*4882a593Smuzhiyun goto err_free_pll;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun init.name = node->name;
260*4882a593Smuzhiyun init.ops = &iproc_arm_pll_ops;
261*4882a593Smuzhiyun init.flags = 0;
262*4882a593Smuzhiyun parent_name = of_clk_get_parent_name(node, 0);
263*4882a593Smuzhiyun init.parent_names = (parent_name ? &parent_name : NULL);
264*4882a593Smuzhiyun init.num_parents = (parent_name ? 1 : 0);
265*4882a593Smuzhiyun pll->hw.init = &init;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun ret = clk_hw_register(NULL, &pll->hw);
268*4882a593Smuzhiyun if (WARN_ON(ret))
269*4882a593Smuzhiyun goto err_iounmap;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll->hw);
272*4882a593Smuzhiyun if (WARN_ON(ret))
273*4882a593Smuzhiyun goto err_clk_unregister;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun return;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun err_clk_unregister:
278*4882a593Smuzhiyun clk_hw_unregister(&pll->hw);
279*4882a593Smuzhiyun err_iounmap:
280*4882a593Smuzhiyun iounmap(pll->base);
281*4882a593Smuzhiyun err_free_pll:
282*4882a593Smuzhiyun kfree(pll);
283*4882a593Smuzhiyun }
284