2 * Copyright (C) 2014 Broadcom Corporation
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/err.h>
17 #include <linux/clk-provider.h>
20 #include <linux/clkdev.h>
21 #include <linux/of_address.h>
23 #define IPROC_CLK_MAX_FREQ_POLICY 0x3
24 #define IPROC_CLK_POLICY_FREQ_OFFSET 0x008
25 #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8
26 #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7
28 #define IPROC_CLK_PLLARMA_OFFSET 0xc00
29 #define IPROC_CLK_PLLARMA_LOCK_SHIFT 28
30 #define IPROC_CLK_PLLARMA_PDIV_SHIFT 24
31 #define IPROC_CLK_PLLARMA_PDIV_MASK 0xf
32 #define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8
33 #define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff
35 #define IPROC_CLK_PLLARMB_OFFSET 0xc04
36 #define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff
38 #define IPROC_CLK_PLLARMC_OFFSET 0xc08
39 #define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8
40 #define IPROC_CLK_PLLARMC_MDIV_MASK 0xff
42 #define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20
43 #define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff
45 #define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24
46 #define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29
47 #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20
48 #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff
49 #define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff
51 #define IPROC_CLK_ARM_DIV_OFFSET 0xe00
52 #define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4
53 #define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf
55 #define IPROC_CLK_POLICY_DBG_OFFSET 0xec0
56 #define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12
57 #define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7
59 enum iproc_arm_pll_fid
{
60 ARM_PLL_FID_CRYSTAL_CLK
= 0,
61 ARM_PLL_FID_SYS_CLK
= 2,
62 ARM_PLL_FID_CH0_SLOW_CLK
= 6,
63 ARM_PLL_FID_CH1_FAST_CLK
= 7
66 struct iproc_arm_pll
{
72 #define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
74 static unsigned int __get_fid(struct iproc_arm_pll
*pll
)
77 unsigned int policy
, fid
, active_fid
;
79 val
= readl(pll
->base
+ IPROC_CLK_ARM_DIV_OFFSET
);
80 if (val
& (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT
))
81 policy
= val
& IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK
;
85 /* something is seriously wrong */
86 BUG_ON(policy
> IPROC_CLK_MAX_FREQ_POLICY
);
88 val
= readl(pll
->base
+ IPROC_CLK_POLICY_FREQ_OFFSET
);
89 fid
= (val
>> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT
* policy
)) &
90 IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK
;
92 val
= readl(pll
->base
+ IPROC_CLK_POLICY_DBG_OFFSET
);
93 active_fid
= IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK
&
94 (val
>> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT
);
95 if (fid
!= active_fid
) {
96 pr_debug("%s: fid override %u->%u\n", __func__
, fid
,
101 pr_debug("%s: active fid: %u\n", __func__
, fid
);
107 * Determine the mdiv (post divider) based on the frequency ID being used.
108 * There are 4 sources that can be used to derive the output clock rate:
111 * - PLL channel 0 (slow clock)
112 * - PLL channel 1 (fast clock)
114 static int __get_mdiv(struct iproc_arm_pll
*pll
)
120 fid
= __get_fid(pll
);
123 case ARM_PLL_FID_CRYSTAL_CLK
:
124 case ARM_PLL_FID_SYS_CLK
:
128 case ARM_PLL_FID_CH0_SLOW_CLK
:
129 val
= readl(pll
->base
+ IPROC_CLK_PLLARMC_OFFSET
);
130 mdiv
= val
& IPROC_CLK_PLLARMC_MDIV_MASK
;
135 case ARM_PLL_FID_CH1_FAST_CLK
:
136 val
= readl(pll
->base
+ IPROC_CLK_PLLARMCTL5_OFFSET
);
137 mdiv
= val
& IPROC_CLK_PLLARMCTL5_H_MDIV_MASK
;
149 static unsigned int __get_ndiv(struct iproc_arm_pll
*pll
)
152 unsigned int ndiv_int
, ndiv_frac
, ndiv
;
154 val
= readl(pll
->base
+ IPROC_CLK_PLLARM_OFFSET_OFFSET
);
155 if (val
& (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT
)) {
157 * offset mode is active. Read the ndiv from the PLLARM OFFSET
160 ndiv_int
= (val
>> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT
) &
161 IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK
;
165 ndiv_frac
= val
& IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK
;
167 /* offset mode not active */
168 val
= readl(pll
->base
+ IPROC_CLK_PLLARMA_OFFSET
);
169 ndiv_int
= (val
>> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT
) &
170 IPROC_CLK_PLLARMA_NDIV_INT_MASK
;
174 val
= readl(pll
->base
+ IPROC_CLK_PLLARMB_OFFSET
);
175 ndiv_frac
= val
& IPROC_CLK_PLLARMB_NDIV_FRAC_MASK
;
178 ndiv
= (ndiv_int
<< 20) | ndiv_frac
;
184 * The output frequency of the ARM PLL is calculated based on the ARM PLL
186 * pdiv = ARM PLL pre-divider
187 * ndiv = ARM PLL multiplier
188 * mdiv = ARM PLL post divider
190 * The frequency is calculated by:
191 * ((ndiv * parent clock rate) / pdiv) / mdiv
193 static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw
*hw
,
194 unsigned long parent_rate
)
196 struct iproc_arm_pll
*pll
= to_iproc_arm_pll(hw
);
202 /* in bypass mode, use parent rate */
203 val
= readl(pll
->base
+ IPROC_CLK_PLLARMC_OFFSET
);
204 if (val
& (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT
)) {
205 pll
->rate
= parent_rate
;
209 /* PLL needs to be locked */
210 val
= readl(pll
->base
+ IPROC_CLK_PLLARMA_OFFSET
);
211 if (!(val
& (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT
))) {
216 pdiv
= (val
>> IPROC_CLK_PLLARMA_PDIV_SHIFT
) &
217 IPROC_CLK_PLLARMA_PDIV_MASK
;
221 ndiv
= __get_ndiv(pll
);
222 mdiv
= __get_mdiv(pll
);
227 pll
->rate
= (ndiv
* parent_rate
) >> 20;
228 pll
->rate
= (pll
->rate
/ pdiv
) / mdiv
;
230 pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__
,
231 pll
->rate
, parent_rate
);
232 pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__
,
233 (unsigned int)(ndiv
>> 20), pdiv
, mdiv
);
238 static const struct clk_ops iproc_arm_pll_ops
= {
239 .recalc_rate
= iproc_arm_pll_recalc_rate
,
242 void __init
iproc_armpll_setup(struct device_node
*node
)
246 struct iproc_arm_pll
*pll
;
247 struct clk_init_data init
;
248 const char *parent_name
;
250 pll
= kzalloc(sizeof(*pll
), GFP_KERNEL
);
254 pll
->base
= of_iomap(node
, 0);
255 if (WARN_ON(!pll
->base
))
258 init
.name
= node
->name
;
259 init
.ops
= &iproc_arm_pll_ops
;
261 parent_name
= of_clk_get_parent_name(node
, 0);
262 init
.parent_names
= (parent_name
? &parent_name
: NULL
);
263 init
.num_parents
= (parent_name
? 1 : 0);
264 pll
->hw
.init
= &init
;
266 clk
= clk_register(NULL
, &pll
->hw
);
267 if (WARN_ON(IS_ERR(clk
)))
270 ret
= of_clk_add_provider(node
, of_clk_src_simple_get
, clk
);
272 goto err_clk_unregister
;