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