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 #include "clk-iproc.h"
25 #define IPROC_CLK_MAX_FREQ_POLICY 0x3
26 #define IPROC_CLK_POLICY_FREQ_OFFSET 0x008
27 #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8
28 #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7
30 #define IPROC_CLK_PLLARMA_OFFSET 0xc00
31 #define IPROC_CLK_PLLARMA_LOCK_SHIFT 28
32 #define IPROC_CLK_PLLARMA_PDIV_SHIFT 24
33 #define IPROC_CLK_PLLARMA_PDIV_MASK 0xf
34 #define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8
35 #define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff
37 #define IPROC_CLK_PLLARMB_OFFSET 0xc04
38 #define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff
40 #define IPROC_CLK_PLLARMC_OFFSET 0xc08
41 #define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8
42 #define IPROC_CLK_PLLARMC_MDIV_MASK 0xff
44 #define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20
45 #define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff
47 #define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24
48 #define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29
49 #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20
50 #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff
51 #define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff
53 #define IPROC_CLK_ARM_DIV_OFFSET 0xe00
54 #define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4
55 #define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf
57 #define IPROC_CLK_POLICY_DBG_OFFSET 0xec0
58 #define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12
59 #define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7
61 enum iproc_arm_pll_fid
{
62 ARM_PLL_FID_CRYSTAL_CLK
= 0,
63 ARM_PLL_FID_SYS_CLK
= 2,
64 ARM_PLL_FID_CH0_SLOW_CLK
= 6,
65 ARM_PLL_FID_CH1_FAST_CLK
= 7
68 struct iproc_arm_pll
{
74 #define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
76 static unsigned int __get_fid(struct iproc_arm_pll
*pll
)
79 unsigned int policy
, fid
, active_fid
;
81 val
= readl(pll
->base
+ IPROC_CLK_ARM_DIV_OFFSET
);
82 if (val
& (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT
))
83 policy
= val
& IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK
;
87 /* something is seriously wrong */
88 BUG_ON(policy
> IPROC_CLK_MAX_FREQ_POLICY
);
90 val
= readl(pll
->base
+ IPROC_CLK_POLICY_FREQ_OFFSET
);
91 fid
= (val
>> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT
* policy
)) &
92 IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK
;
94 val
= readl(pll
->base
+ IPROC_CLK_POLICY_DBG_OFFSET
);
95 active_fid
= IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK
&
96 (val
>> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT
);
97 if (fid
!= active_fid
) {
98 pr_debug("%s: fid override %u->%u\n", __func__
, fid
,
103 pr_debug("%s: active fid: %u\n", __func__
, fid
);
109 * Determine the mdiv (post divider) based on the frequency ID being used.
110 * There are 4 sources that can be used to derive the output clock rate:
113 * - PLL channel 0 (slow clock)
114 * - PLL channel 1 (fast clock)
116 static int __get_mdiv(struct iproc_arm_pll
*pll
)
122 fid
= __get_fid(pll
);
125 case ARM_PLL_FID_CRYSTAL_CLK
:
126 case ARM_PLL_FID_SYS_CLK
:
130 case ARM_PLL_FID_CH0_SLOW_CLK
:
131 val
= readl(pll
->base
+ IPROC_CLK_PLLARMC_OFFSET
);
132 mdiv
= val
& IPROC_CLK_PLLARMC_MDIV_MASK
;
137 case ARM_PLL_FID_CH1_FAST_CLK
:
138 val
= readl(pll
->base
+ IPROC_CLK_PLLARMCTL5_OFFSET
);
139 mdiv
= val
& IPROC_CLK_PLLARMCTL5_H_MDIV_MASK
;
151 static unsigned int __get_ndiv(struct iproc_arm_pll
*pll
)
154 unsigned int ndiv_int
, ndiv_frac
, ndiv
;
156 val
= readl(pll
->base
+ IPROC_CLK_PLLARM_OFFSET_OFFSET
);
157 if (val
& (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT
)) {
159 * offset mode is active. Read the ndiv from the PLLARM OFFSET
162 ndiv_int
= (val
>> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT
) &
163 IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK
;
167 ndiv_frac
= val
& IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK
;
169 /* offset mode not active */
170 val
= readl(pll
->base
+ IPROC_CLK_PLLARMA_OFFSET
);
171 ndiv_int
= (val
>> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT
) &
172 IPROC_CLK_PLLARMA_NDIV_INT_MASK
;
176 val
= readl(pll
->base
+ IPROC_CLK_PLLARMB_OFFSET
);
177 ndiv_frac
= val
& IPROC_CLK_PLLARMB_NDIV_FRAC_MASK
;
180 ndiv
= (ndiv_int
<< 20) | ndiv_frac
;
186 * The output frequency of the ARM PLL is calculated based on the ARM PLL
188 * pdiv = ARM PLL pre-divider
189 * ndiv = ARM PLL multiplier
190 * mdiv = ARM PLL post divider
192 * The frequency is calculated by:
193 * ((ndiv * parent clock rate) / pdiv) / mdiv
195 static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw
*hw
,
196 unsigned long parent_rate
)
198 struct iproc_arm_pll
*pll
= to_iproc_arm_pll(hw
);
204 /* in bypass mode, use parent rate */
205 val
= readl(pll
->base
+ IPROC_CLK_PLLARMC_OFFSET
);
206 if (val
& (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT
)) {
207 pll
->rate
= parent_rate
;
211 /* PLL needs to be locked */
212 val
= readl(pll
->base
+ IPROC_CLK_PLLARMA_OFFSET
);
213 if (!(val
& (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT
))) {
218 pdiv
= (val
>> IPROC_CLK_PLLARMA_PDIV_SHIFT
) &
219 IPROC_CLK_PLLARMA_PDIV_MASK
;
223 ndiv
= __get_ndiv(pll
);
224 mdiv
= __get_mdiv(pll
);
229 pll
->rate
= (ndiv
* parent_rate
) >> 20;
230 pll
->rate
= (pll
->rate
/ pdiv
) / mdiv
;
232 pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__
,
233 pll
->rate
, parent_rate
);
234 pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__
,
235 (unsigned int)(ndiv
>> 20), pdiv
, mdiv
);
240 static const struct clk_ops iproc_arm_pll_ops
= {
241 .recalc_rate
= iproc_arm_pll_recalc_rate
,
244 void __init
iproc_armpll_setup(struct device_node
*node
)
247 struct iproc_arm_pll
*pll
;
248 struct clk_init_data init
;
249 const char *parent_name
;
251 pll
= kzalloc(sizeof(*pll
), GFP_KERNEL
);
255 pll
->base
= of_iomap(node
, 0);
256 if (WARN_ON(!pll
->base
))
259 init
.name
= node
->name
;
260 init
.ops
= &iproc_arm_pll_ops
;
262 parent_name
= of_clk_get_parent_name(node
, 0);
263 init
.parent_names
= (parent_name
? &parent_name
: NULL
);
264 init
.num_parents
= (parent_name
? 1 : 0);
265 pll
->hw
.init
= &init
;
267 ret
= clk_hw_register(NULL
, &pll
->hw
);
271 ret
= of_clk_add_hw_provider(node
, of_clk_hw_simple_get
, &pll
->hw
);
273 goto err_clk_unregister
;
278 clk_hw_unregister(&pll
->hw
);