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
54struct 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
63static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
64
65static 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
83static 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
102static 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
119static 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
150static 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
176static 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
212static const struct clk_ops berlin2_div_rate_ops = {
213	.recalc_rate	= berlin2_div_recalc_rate,
214};
215
216static const struct clk_ops berlin2_div_gate_ops = {
217	.is_enabled	= berlin2_div_is_enabled,
218	.enable		= berlin2_div_enable,
219	.disable	= berlin2_div_disable,
220};
221
222static const struct clk_ops berlin2_div_mux_ops = {
223	.set_parent	= berlin2_div_set_parent,
224	.get_parent	= berlin2_div_get_parent,
225};
226
227struct clk_hw * __init
228berlin2_div_register(const struct berlin2_div_map *map,
229		     void __iomem *base, const char *name, u8 div_flags,
230		     const char **parent_names, int num_parents,
231		     unsigned long flags, spinlock_t *lock)
232{
233	const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
234	const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
235	const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
236	struct berlin2_div *div;
237
238	div = kzalloc(sizeof(*div), GFP_KERNEL);
239	if (!div)
240		return ERR_PTR(-ENOMEM);
241
242	/* copy div_map to allow __initconst */
243	memcpy(&div->map, map, sizeof(*map));
244	div->base = base;
245	div->lock = lock;
246
247	if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
248		gate_ops = NULL;
249	if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
250		mux_ops = NULL;
251
252	return clk_hw_register_composite(NULL, name, parent_names, num_parents,
253				      &div->hw, mux_ops, &div->hw, rate_ops,
254				      &div->hw, gate_ops, flags);
255}
256