Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/berlin/berlin2-div.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (c) 2014 Marvell Technology Group Ltd.
4
*
5
* Alexandre Belloni <[email protected]>
6
* Sebastian Hesselbarth <[email protected]>
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
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
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
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
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
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
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
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
258