1 // SPDX-License-Identifier: GPL-2.0
3 * Marvell Dove PMU Core PLL divider driver
5 * Cleaned up by substantially rewriting, and converted to DT by
6 * Russell King. Origin is not known.
8 #include <linux/clk-provider.h>
9 #include <linux/delay.h>
11 #include <linux/kernel.h>
13 #include <linux/of_address.h>
15 #include "dove-divider.h"
32 DIV_CTRL1_N_RESET_MASK
= BIT(10),
35 #define to_dove_clk(hw) container_of(hw, struct dove_clk, hw)
37 static void dove_load_divider(void __iomem
*base
, u32 val
, u32 mask
, u32 load
)
41 v
= readl_relaxed(base
+ DIV_CTRL1
) | DIV_CTRL1_N_RESET_MASK
;
42 writel_relaxed(v
, base
+ DIV_CTRL1
);
44 v
= (readl_relaxed(base
+ DIV_CTRL0
) & ~(mask
| load
)) | val
;
45 writel_relaxed(v
, base
+ DIV_CTRL0
);
46 writel_relaxed(v
| load
, base
+ DIV_CTRL0
);
48 writel_relaxed(v
, base
+ DIV_CTRL0
);
51 static unsigned int dove_get_divider(struct dove_clk
*dc
)
56 val
= readl_relaxed(dc
->base
+ DIV_CTRL0
);
57 val
>>= dc
->div_bit_start
;
59 divider
= val
& ~(~0 << dc
->div_bit_size
);
61 if (dc
->divider_table
)
62 divider
= dc
->divider_table
[divider
];
67 static int dove_calc_divider(const struct dove_clk
*dc
, unsigned long rate
,
68 unsigned long parent_rate
, bool set
)
70 unsigned int divider
, max
;
72 divider
= DIV_ROUND_CLOSEST(parent_rate
, rate
);
74 if (dc
->divider_table
) {
77 for (i
= 0; dc
->divider_table
[i
]; i
++)
78 if (divider
== dc
->divider_table
[i
]) {
83 if (!dc
->divider_table
[i
])
86 max
= 1 << dc
->div_bit_size
;
88 if (set
&& (divider
== 0 || divider
>= max
))
92 else if (divider
== 0)
99 static unsigned long dove_recalc_rate(struct clk_hw
*hw
, unsigned long parent
)
101 struct dove_clk
*dc
= to_dove_clk(hw
);
102 unsigned int divider
= dove_get_divider(dc
);
103 unsigned long rate
= DIV_ROUND_CLOSEST(parent
, divider
);
105 pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
106 __func__
, dc
->name
, divider
, parent
, rate
);
111 static long dove_round_rate(struct clk_hw
*hw
, unsigned long rate
,
112 unsigned long *parent
)
114 struct dove_clk
*dc
= to_dove_clk(hw
);
115 unsigned long parent_rate
= *parent
;
118 divider
= dove_calc_divider(dc
, rate
, parent_rate
, false);
122 rate
= DIV_ROUND_CLOSEST(parent_rate
, divider
);
124 pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
125 __func__
, dc
->name
, divider
, parent_rate
, rate
);
130 static int dove_set_clock(struct clk_hw
*hw
, unsigned long rate
,
131 unsigned long parent_rate
)
133 struct dove_clk
*dc
= to_dove_clk(hw
);
137 divider
= dove_calc_divider(dc
, rate
, parent_rate
, true);
141 pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
142 __func__
, dc
->name
, divider
, parent_rate
, rate
);
144 div
= (u32
)divider
<< dc
->div_bit_start
;
145 mask
= ~(~0 << dc
->div_bit_size
) << dc
->div_bit_start
;
146 load
= BIT(dc
->div_bit_load
);
149 dove_load_divider(dc
->base
, div
, mask
, load
);
150 spin_unlock(dc
->lock
);
155 static const struct clk_ops dove_divider_ops
= {
156 .set_rate
= dove_set_clock
,
157 .round_rate
= dove_round_rate
,
158 .recalc_rate
= dove_recalc_rate
,
161 static struct clk
*clk_register_dove_divider(struct device
*dev
,
162 struct dove_clk
*dc
, const char **parent_names
, size_t num_parents
,
166 struct clk_init_data init
= {
168 .ops
= &dove_divider_ops
,
169 .parent_names
= parent_names
,
170 .num_parents
= num_parents
,
173 strlcpy(name
, dc
->name
, sizeof(name
));
177 dc
->div_bit_size
= dc
->div_bit_end
- dc
->div_bit_start
+ 1;
179 return clk_register(dev
, &dc
->hw
);
182 static DEFINE_SPINLOCK(dove_divider_lock
);
184 static u32 axi_divider
[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0};
186 static struct dove_clk dove_hw_clocks
[4] = {
189 .lock
= &dove_divider_lock
,
193 .divider_table
= axi_divider
,
196 .lock
= &dove_divider_lock
,
202 .lock
= &dove_divider_lock
,
208 .lock
= &dove_divider_lock
,
215 static const char *core_pll
[] = {
219 static int dove_divider_init(struct device
*dev
, void __iomem
*base
,
226 * Create the core PLL clock. We treat this as a fixed rate
227 * clock as we don't know any better, and documentation is sparse.
229 clk
= clk_register_fixed_rate(dev
, core_pll
[0], NULL
, 0, 2000000000UL);
233 for (i
= 0; i
< ARRAY_SIZE(dove_hw_clocks
); i
++)
234 clks
[i
] = clk_register_dove_divider(dev
, &dove_hw_clocks
[i
],
236 ARRAY_SIZE(core_pll
), base
);
241 static struct clk
*dove_divider_clocks
[4];
243 static struct clk_onecell_data dove_divider_data
= {
244 .clks
= dove_divider_clocks
,
245 .clk_num
= ARRAY_SIZE(dove_divider_clocks
),
248 void __init
dove_divider_clk_init(struct device_node
*np
)
252 base
= of_iomap(np
, 0);
256 if (WARN_ON(dove_divider_init(NULL
, base
, dove_divider_clocks
))) {
261 of_clk_add_provider(np
, of_clk_src_onecell_get
, &dove_divider_data
);