xref: /openbmc/linux/drivers/clk/berlin/berlin2-div.c (revision 321437f3)
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