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