1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2014 Marvell Technology Group Ltd.
4 *
5 * Alexandre Belloni <alexandre.belloni@free-electrons.com>
6 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
7 */
8 #include <linux/bitops.h>
9 #include <linux/clk-provider.h>
10 #include <linux/io.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/slab.h>
14 #include <linux/spinlock.h>
15
16 #include "berlin2-div.h"
17
18 /*
19 * Clock dividers in Berlin2 SoCs comprise a complex cell to select
20 * input pll and divider. The virtual structure as it is used in Marvell
21 * BSP code can be seen as:
22 *
23 * +---+
24 * pll0 --------------->| 0 | +---+
25 * +---+ |(B)|--+--------------->| 0 | +---+
26 * pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+
27 * pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|->
28 * ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+
29 * ... -->| | +-->|(D) 1:3 |----------+ +---+
30 * pll1.N -->| N | +---------
31 * +---+
32 *
33 * (A) input pll clock mux controlled by <PllSelect[1:n]>
34 * (B) input pll bypass mux controlled by <PllSwitch>
35 * (C) programmable clock divider controlled by <Select[1:n]>
36 * (D) constant div-by-3 clock divider
37 * (E) programmable clock divider bypass controlled by <Switch>
38 * (F) constant div-by-3 clock mux controlled by <D3Switch>
39 * (G) clock gate controlled by <Enable>
40 *
41 * For whatever reason, above control signals come in two flavors:
42 * - single register dividers with all bits in one register
43 * - shared register dividers with bits spread over multiple registers
44 * (including signals for the same cell spread over consecutive registers)
45 *
46 * Also, clock gate and pll mux is not available on every div cell, so
47 * we have to deal with those, too. We reuse common clock composite driver
48 * for it.
49 */
50
51 #define PLL_SELECT_MASK 0x7
52 #define DIV_SELECT_MASK 0x7
53
54 struct berlin2_div {
55 struct clk_hw hw;
56 void __iomem *base;
57 struct berlin2_div_map map;
58 spinlock_t *lock;
59 };
60
61 #define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
62
63 static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
64
berlin2_div_is_enabled(struct clk_hw * hw)65 static int berlin2_div_is_enabled(struct clk_hw *hw)
66 {
67 struct berlin2_div *div = to_berlin2_div(hw);
68 struct berlin2_div_map *map = &div->map;
69 u32 reg;
70
71 if (div->lock)
72 spin_lock(div->lock);
73
74 reg = readl_relaxed(div->base + map->gate_offs);
75 reg >>= map->gate_shift;
76
77 if (div->lock)
78 spin_unlock(div->lock);
79
80 return (reg & 0x1);
81 }
82
berlin2_div_enable(struct clk_hw * hw)83 static int berlin2_div_enable(struct clk_hw *hw)
84 {
85 struct berlin2_div *div = to_berlin2_div(hw);
86 struct berlin2_div_map *map = &div->map;
87 u32 reg;
88
89 if (div->lock)
90 spin_lock(div->lock);
91
92 reg = readl_relaxed(div->base + map->gate_offs);
93 reg |= BIT(map->gate_shift);
94 writel_relaxed(reg, div->base + map->gate_offs);
95
96 if (div->lock)
97 spin_unlock(div->lock);
98
99 return 0;
100 }
101
berlin2_div_disable(struct clk_hw * hw)102 static void berlin2_div_disable(struct clk_hw *hw)
103 {
104 struct berlin2_div *div = to_berlin2_div(hw);
105 struct berlin2_div_map *map = &div->map;
106 u32 reg;
107
108 if (div->lock)
109 spin_lock(div->lock);
110
111 reg = readl_relaxed(div->base + map->gate_offs);
112 reg &= ~BIT(map->gate_shift);
113 writel_relaxed(reg, div->base + map->gate_offs);
114
115 if (div->lock)
116 spin_unlock(div->lock);
117 }
118
berlin2_div_set_parent(struct clk_hw * hw,u8 index)119 static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
120 {
121 struct berlin2_div *div = to_berlin2_div(hw);
122 struct berlin2_div_map *map = &div->map;
123 u32 reg;
124
125 if (div->lock)
126 spin_lock(div->lock);
127
128 /* index == 0 is PLL_SWITCH */
129 reg = readl_relaxed(div->base + map->pll_switch_offs);
130 if (index == 0)
131 reg &= ~BIT(map->pll_switch_shift);
132 else
133 reg |= BIT(map->pll_switch_shift);
134 writel_relaxed(reg, div->base + map->pll_switch_offs);
135
136 /* index > 0 is PLL_SELECT */
137 if (index > 0) {
138 reg = readl_relaxed(div->base + map->pll_select_offs);
139 reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
140 reg |= (index - 1) << map->pll_select_shift;
141 writel_relaxed(reg, div->base + map->pll_select_offs);
142 }
143
144 if (div->lock)
145 spin_unlock(div->lock);
146
147 return 0;
148 }
149
berlin2_div_get_parent(struct clk_hw * hw)150 static u8 berlin2_div_get_parent(struct clk_hw *hw)
151 {
152 struct berlin2_div *div = to_berlin2_div(hw);
153 struct berlin2_div_map *map = &div->map;
154 u32 reg;
155 u8 index = 0;
156
157 if (div->lock)
158 spin_lock(div->lock);
159
160 /* PLL_SWITCH == 0 is index 0 */
161 reg = readl_relaxed(div->base + map->pll_switch_offs);
162 reg &= BIT(map->pll_switch_shift);
163 if (reg) {
164 reg = readl_relaxed(div->base + map->pll_select_offs);
165 reg >>= map->pll_select_shift;
166 reg &= PLL_SELECT_MASK;
167 index = 1 + reg;
168 }
169
170 if (div->lock)
171 spin_unlock(div->lock);
172
173 return index;
174 }
175
berlin2_div_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)176 static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
177 unsigned long parent_rate)
178 {
179 struct berlin2_div *div = to_berlin2_div(hw);
180 struct berlin2_div_map *map = &div->map;
181 u32 divsw, div3sw, divider = 1;
182
183 if (div->lock)
184 spin_lock(div->lock);
185
186 divsw = readl_relaxed(div->base + map->div_switch_offs) &
187 (1 << map->div_switch_shift);
188 div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
189 (1 << map->div3_switch_shift);
190
191 /* constant divide-by-3 (dominant) */
192 if (div3sw != 0) {
193 divider = 3;
194 /* divider can be bypassed with DIV_SWITCH == 0 */
195 } else if (divsw == 0) {
196 divider = 1;
197 /* clock divider determined by DIV_SELECT */
198 } else {
199 u32 reg;
200 reg = readl_relaxed(div->base + map->div_select_offs);
201 reg >>= map->div_select_shift;
202 reg &= DIV_SELECT_MASK;
203 divider = clk_div[reg];
204 }
205
206 if (div->lock)
207 spin_unlock(div->lock);
208
209 return parent_rate / divider;
210 }
211
212 static const struct clk_ops berlin2_div_rate_ops = {
213 .determine_rate = clk_hw_determine_rate_no_reparent,
214 .recalc_rate = berlin2_div_recalc_rate,
215 };
216
217 static const struct clk_ops berlin2_div_gate_ops = {
218 .is_enabled = berlin2_div_is_enabled,
219 .enable = berlin2_div_enable,
220 .disable = berlin2_div_disable,
221 };
222
223 static const struct clk_ops berlin2_div_mux_ops = {
224 .set_parent = berlin2_div_set_parent,
225 .get_parent = berlin2_div_get_parent,
226 };
227
228 struct clk_hw * __init
berlin2_div_register(const struct berlin2_div_map * map,void __iomem * base,const char * name,u8 div_flags,const char ** parent_names,int num_parents,unsigned long flags,spinlock_t * lock)229 berlin2_div_register(const struct berlin2_div_map *map,
230 void __iomem *base, const char *name, u8 div_flags,
231 const char **parent_names, int num_parents,
232 unsigned long flags, spinlock_t *lock)
233 {
234 const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
235 const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
236 const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
237 struct berlin2_div *div;
238
239 div = kzalloc(sizeof(*div), GFP_KERNEL);
240 if (!div)
241 return ERR_PTR(-ENOMEM);
242
243 /* copy div_map to allow __initconst */
244 memcpy(&div->map, map, sizeof(*map));
245 div->base = base;
246 div->lock = lock;
247
248 if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
249 gate_ops = NULL;
250 if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
251 mux_ops = NULL;
252
253 return clk_hw_register_composite(NULL, name, parent_names, num_parents,
254 &div->hw, mux_ops, &div->hw, rate_ops,
255 &div->hw, gate_ops, flags);
256 }
257