Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/clk-lan966x.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Microchip LAN966x SoC Clock driver.
4
*
5
* Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries
6
*
7
* Author: Kavyasree Kotagiri <[email protected]>
8
*/
9
10
#include <linux/bitfield.h>
11
#include <linux/clk-provider.h>
12
#include <linux/io.h>
13
#include <linux/kernel.h>
14
#include <linux/module.h>
15
#include <linux/of.h>
16
#include <linux/platform_device.h>
17
#include <linux/slab.h>
18
19
#include <dt-bindings/clock/microchip,lan966x.h>
20
21
#define GCK_ENA BIT(0)
22
#define GCK_SRC_SEL GENMASK(9, 8)
23
#define GCK_PRESCALER GENMASK(23, 16)
24
25
#define DIV_MAX 255
26
27
static const char * const lan966x_clk_names[] = {
28
"qspi0", "qspi1", "qspi2", "sdmmc0",
29
"pi", "mcan0", "mcan1", "flexcom0",
30
"flexcom1", "flexcom2", "flexcom3",
31
"flexcom4", "timer1", "usb_refclk",
32
};
33
34
static const char * const lan969x_clk_names[] = {
35
"qspi0", "qspi2", "sdmmc0", "sdmmc1",
36
"mcan0", "mcan1", "flexcom0",
37
"flexcom1", "flexcom2", "flexcom3",
38
"timer1", "usb_refclk",
39
};
40
41
struct lan966x_gck {
42
struct clk_hw hw;
43
void __iomem *reg;
44
};
45
#define to_lan966x_gck(hw) container_of(hw, struct lan966x_gck, hw)
46
47
static const struct clk_parent_data lan966x_gck_pdata[] = {
48
{ .fw_name = "cpu", },
49
{ .fw_name = "ddr", },
50
{ .fw_name = "sys", },
51
};
52
53
static struct clk_init_data init = {
54
.parent_data = lan966x_gck_pdata,
55
.num_parents = ARRAY_SIZE(lan966x_gck_pdata),
56
};
57
58
struct clk_gate_soc_desc {
59
const char *name;
60
int bit_idx;
61
};
62
63
static const struct clk_gate_soc_desc lan966x_clk_gate_desc[] = {
64
{ "uhphs", 11 },
65
{ "udphs", 10 },
66
{ "mcramc", 9 },
67
{ "hmatrix", 8 },
68
{ }
69
};
70
71
static const struct clk_gate_soc_desc lan969x_clk_gate_desc[] = {
72
{ "usb_drd", 10 },
73
{ "mcramc", 9 },
74
{ "hmatrix", 8 },
75
{ }
76
};
77
78
struct lan966x_match_data {
79
char *name;
80
const char * const *clk_name;
81
const struct clk_gate_soc_desc *clk_gate_desc;
82
u8 num_generic_clks;
83
u8 num_total_clks;
84
};
85
86
static struct lan966x_match_data lan966x_desc = {
87
.name = "lan966x",
88
.clk_name = lan966x_clk_names,
89
.clk_gate_desc = lan966x_clk_gate_desc,
90
.num_total_clks = 18,
91
.num_generic_clks = 14,
92
};
93
94
static struct lan966x_match_data lan969x_desc = {
95
.name = "lan969x",
96
.clk_name = lan969x_clk_names,
97
.clk_gate_desc = lan969x_clk_gate_desc,
98
.num_total_clks = 15,
99
.num_generic_clks = 12,
100
};
101
102
static DEFINE_SPINLOCK(clk_gate_lock);
103
static void __iomem *base;
104
105
static int lan966x_gck_enable(struct clk_hw *hw)
106
{
107
struct lan966x_gck *gck = to_lan966x_gck(hw);
108
u32 val = readl(gck->reg);
109
110
val |= GCK_ENA;
111
writel(val, gck->reg);
112
113
return 0;
114
}
115
116
static void lan966x_gck_disable(struct clk_hw *hw)
117
{
118
struct lan966x_gck *gck = to_lan966x_gck(hw);
119
u32 val = readl(gck->reg);
120
121
val &= ~GCK_ENA;
122
writel(val, gck->reg);
123
}
124
125
static int lan966x_gck_set_rate(struct clk_hw *hw,
126
unsigned long rate,
127
unsigned long parent_rate)
128
{
129
struct lan966x_gck *gck = to_lan966x_gck(hw);
130
u32 div, val = readl(gck->reg);
131
132
if (rate == 0 || parent_rate == 0)
133
return -EINVAL;
134
135
/* Set Prescalar */
136
div = parent_rate / rate;
137
val &= ~GCK_PRESCALER;
138
val |= FIELD_PREP(GCK_PRESCALER, (div - 1));
139
writel(val, gck->reg);
140
141
return 0;
142
}
143
144
static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw,
145
unsigned long parent_rate)
146
{
147
struct lan966x_gck *gck = to_lan966x_gck(hw);
148
u32 div, val = readl(gck->reg);
149
150
div = FIELD_GET(GCK_PRESCALER, val);
151
152
return parent_rate / (div + 1);
153
}
154
155
static int lan966x_gck_determine_rate(struct clk_hw *hw,
156
struct clk_rate_request *req)
157
{
158
struct clk_hw *parent;
159
int i;
160
161
for (i = 0; i < clk_hw_get_num_parents(hw); ++i) {
162
parent = clk_hw_get_parent_by_index(hw, i);
163
if (!parent)
164
continue;
165
166
/* Allowed prescaler divider range is 0-255 */
167
if (clk_hw_get_rate(parent) / req->rate <= DIV_MAX) {
168
req->best_parent_hw = parent;
169
req->best_parent_rate = clk_hw_get_rate(parent);
170
171
return 0;
172
}
173
}
174
175
return -EINVAL;
176
}
177
178
static u8 lan966x_gck_get_parent(struct clk_hw *hw)
179
{
180
struct lan966x_gck *gck = to_lan966x_gck(hw);
181
u32 val = readl(gck->reg);
182
183
return FIELD_GET(GCK_SRC_SEL, val);
184
}
185
186
static int lan966x_gck_set_parent(struct clk_hw *hw, u8 index)
187
{
188
struct lan966x_gck *gck = to_lan966x_gck(hw);
189
u32 val = readl(gck->reg);
190
191
val &= ~GCK_SRC_SEL;
192
val |= FIELD_PREP(GCK_SRC_SEL, index);
193
writel(val, gck->reg);
194
195
return 0;
196
}
197
198
static const struct clk_ops lan966x_gck_ops = {
199
.enable = lan966x_gck_enable,
200
.disable = lan966x_gck_disable,
201
.set_rate = lan966x_gck_set_rate,
202
.recalc_rate = lan966x_gck_recalc_rate,
203
.determine_rate = lan966x_gck_determine_rate,
204
.set_parent = lan966x_gck_set_parent,
205
.get_parent = lan966x_gck_get_parent,
206
};
207
208
static struct clk_hw *lan966x_gck_clk_register(struct device *dev, int i)
209
{
210
struct lan966x_gck *priv;
211
int ret;
212
213
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
214
if (!priv)
215
return ERR_PTR(-ENOMEM);
216
217
priv->reg = base + (i * 4);
218
priv->hw.init = &init;
219
ret = devm_clk_hw_register(dev, &priv->hw);
220
if (ret)
221
return ERR_PTR(ret);
222
223
return &priv->hw;
224
};
225
226
static int lan966x_gate_clk_register(struct device *dev,
227
const struct lan966x_match_data *data,
228
struct clk_hw_onecell_data *hw_data,
229
void __iomem *gate_base)
230
{
231
for (int i = data->num_generic_clks; i < data->num_total_clks; ++i) {
232
int idx = i - data->num_generic_clks;
233
const struct clk_gate_soc_desc *desc;
234
235
desc = &data->clk_gate_desc[idx];
236
237
hw_data->hws[i] =
238
devm_clk_hw_register_gate(dev, desc->name,
239
data->name, 0, gate_base,
240
desc->bit_idx,
241
0, &clk_gate_lock);
242
243
if (IS_ERR(hw_data->hws[i]))
244
return dev_err_probe(dev, PTR_ERR(hw_data->hws[i]),
245
"failed to register %s clock\n",
246
desc->name);
247
}
248
249
return 0;
250
}
251
252
static int lan966x_clk_probe(struct platform_device *pdev)
253
{
254
const struct lan966x_match_data *data;
255
struct clk_hw_onecell_data *hw_data;
256
struct device *dev = &pdev->dev;
257
void __iomem *gate_base;
258
struct resource *res;
259
int i, ret;
260
261
data = device_get_match_data(dev);
262
if (!data)
263
return -EINVAL;
264
265
hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, data->num_total_clks),
266
GFP_KERNEL);
267
if (!hw_data)
268
return -ENOMEM;
269
270
base = devm_platform_ioremap_resource(pdev, 0);
271
if (IS_ERR(base))
272
return PTR_ERR(base);
273
274
init.ops = &lan966x_gck_ops;
275
276
hw_data->num = data->num_generic_clks;
277
278
for (i = 0; i < data->num_generic_clks; i++) {
279
init.name = data->clk_name[i];
280
hw_data->hws[i] = lan966x_gck_clk_register(dev, i);
281
if (IS_ERR(hw_data->hws[i])) {
282
dev_err(dev, "failed to register %s clock\n",
283
init.name);
284
return PTR_ERR(hw_data->hws[i]);
285
}
286
}
287
288
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
289
if (res) {
290
gate_base = devm_ioremap_resource(&pdev->dev, res);
291
if (IS_ERR(gate_base))
292
return PTR_ERR(gate_base);
293
294
hw_data->num = data->num_total_clks;
295
296
ret = lan966x_gate_clk_register(dev, data, hw_data, gate_base);
297
if (ret)
298
return ret;
299
}
300
301
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
302
}
303
304
static const struct of_device_id lan966x_clk_dt_ids[] = {
305
{ .compatible = "microchip,lan966x-gck", .data = &lan966x_desc },
306
{ .compatible = "microchip,lan9691-gck", .data = &lan969x_desc },
307
{ }
308
};
309
MODULE_DEVICE_TABLE(of, lan966x_clk_dt_ids);
310
311
static struct platform_driver lan966x_clk_driver = {
312
.probe = lan966x_clk_probe,
313
.driver = {
314
.name = "lan966x-clk",
315
.of_match_table = lan966x_clk_dt_ids,
316
},
317
};
318
module_platform_driver(lan966x_clk_driver);
319
320
MODULE_AUTHOR("Kavyasree Kotagiri <[email protected]>");
321
MODULE_DESCRIPTION("LAN966X clock driver");
322
MODULE_LICENSE("GPL v2");
323
324