Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/at91/at91sam9x5.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <linux/clk-provider.h>
3
#include <linux/mfd/syscon.h>
4
#include <linux/slab.h>
5
6
#include <dt-bindings/clock/at91.h>
7
8
#include "pmc.h"
9
10
static DEFINE_SPINLOCK(mck_lock);
11
12
static const struct clk_master_characteristics mck_characteristics = {
13
.output = { .min = 0, .max = 133333333 },
14
.divisors = { 1, 2, 4, 3 },
15
.have_div3_pres = 1,
16
};
17
18
static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
19
20
static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
21
22
static const struct clk_range plla_outputs[] = {
23
{ .min = 745000000, .max = 800000000 },
24
{ .min = 695000000, .max = 750000000 },
25
{ .min = 645000000, .max = 700000000 },
26
{ .min = 595000000, .max = 650000000 },
27
{ .min = 545000000, .max = 600000000 },
28
{ .min = 495000000, .max = 555000000 },
29
{ .min = 445000000, .max = 500000000 },
30
{ .min = 400000000, .max = 450000000 },
31
};
32
33
static const struct clk_pll_characteristics plla_characteristics = {
34
.input = { .min = 2000000, .max = 32000000 },
35
.num_output = ARRAY_SIZE(plla_outputs),
36
.output = plla_outputs,
37
.icpll = plla_icpll,
38
.out = plla_out,
39
};
40
41
static const struct {
42
char *n;
43
char *p;
44
unsigned long flags;
45
u8 id;
46
} at91sam9x5_systemck[] = {
47
/*
48
* ddrck feeds DDR controller and is enabled by bootloader thus we need
49
* to keep it enabled in case there is no Linux consumer for it.
50
*/
51
{ .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
52
{ .n = "smdck", .p = "smdclk", .id = 4 },
53
{ .n = "uhpck", .p = "usbck", .id = 6 },
54
{ .n = "udpck", .p = "usbck", .id = 7 },
55
{ .n = "pck0", .p = "prog0", .id = 8 },
56
{ .n = "pck1", .p = "prog1", .id = 9 },
57
};
58
59
static const struct clk_pcr_layout at91sam9x5_pcr_layout = {
60
.offset = 0x10c,
61
.cmd = BIT(12),
62
.pid_mask = GENMASK(5, 0),
63
.div_mask = GENMASK(17, 16),
64
};
65
66
struct pck {
67
char *n;
68
u8 id;
69
};
70
71
static const struct pck at91sam9x5_periphck[] = {
72
{ .n = "pioAB_clk", .id = 2, },
73
{ .n = "pioCD_clk", .id = 3, },
74
{ .n = "smd_clk", .id = 4, },
75
{ .n = "usart0_clk", .id = 5, },
76
{ .n = "usart1_clk", .id = 6, },
77
{ .n = "usart2_clk", .id = 7, },
78
{ .n = "twi0_clk", .id = 9, },
79
{ .n = "twi1_clk", .id = 10, },
80
{ .n = "twi2_clk", .id = 11, },
81
{ .n = "mci0_clk", .id = 12, },
82
{ .n = "spi0_clk", .id = 13, },
83
{ .n = "spi1_clk", .id = 14, },
84
{ .n = "uart0_clk", .id = 15, },
85
{ .n = "uart1_clk", .id = 16, },
86
{ .n = "tcb0_clk", .id = 17, },
87
{ .n = "pwm_clk", .id = 18, },
88
{ .n = "adc_clk", .id = 19, },
89
{ .n = "dma0_clk", .id = 20, },
90
{ .n = "dma1_clk", .id = 21, },
91
{ .n = "uhphs_clk", .id = 22, },
92
{ .n = "udphs_clk", .id = 23, },
93
{ .n = "mci1_clk", .id = 26, },
94
{ .n = "ssc0_clk", .id = 28, },
95
};
96
97
static const struct pck at91sam9g15_periphck[] = {
98
{ .n = "lcdc_clk", .id = 25, },
99
{ /* sentinel */}
100
};
101
102
static const struct pck at91sam9g25_periphck[] = {
103
{ .n = "usart3_clk", .id = 8, },
104
{ .n = "macb0_clk", .id = 24, },
105
{ .n = "isi_clk", .id = 25, },
106
{ /* sentinel */}
107
};
108
109
static const struct pck at91sam9g35_periphck[] = {
110
{ .n = "macb0_clk", .id = 24, },
111
{ .n = "lcdc_clk", .id = 25, },
112
{ /* sentinel */}
113
};
114
115
static const struct pck at91sam9x25_periphck[] = {
116
{ .n = "usart3_clk", .id = 8, },
117
{ .n = "macb0_clk", .id = 24, },
118
{ .n = "macb1_clk", .id = 27, },
119
{ .n = "can0_clk", .id = 29, },
120
{ .n = "can1_clk", .id = 30, },
121
{ /* sentinel */}
122
};
123
124
static const struct pck at91sam9x35_periphck[] = {
125
{ .n = "macb0_clk", .id = 24, },
126
{ .n = "lcdc_clk", .id = 25, },
127
{ .n = "can0_clk", .id = 29, },
128
{ .n = "can1_clk", .id = 30, },
129
{ /* sentinel */}
130
};
131
132
static void __init at91sam9x5_pmc_setup(struct device_node *np,
133
const struct pck *extra_pcks,
134
bool has_lcdck)
135
{
136
struct clk_range range = CLK_RANGE(0, 0);
137
const char *slck_name, *mainxtal_name;
138
struct pmc_data *at91sam9x5_pmc;
139
const char *parent_names[6];
140
struct regmap *regmap;
141
struct clk_hw *hw;
142
int i;
143
bool bypass;
144
145
i = of_property_match_string(np, "clock-names", "slow_clk");
146
if (i < 0)
147
return;
148
149
slck_name = of_clk_get_parent_name(np, i);
150
151
i = of_property_match_string(np, "clock-names", "main_xtal");
152
if (i < 0)
153
return;
154
mainxtal_name = of_clk_get_parent_name(np, i);
155
156
regmap = device_node_to_regmap(np);
157
if (IS_ERR(regmap))
158
return;
159
160
at91sam9x5_pmc = pmc_data_allocate(PMC_PLLACK + 1,
161
nck(at91sam9x5_systemck), 31, 0, 2);
162
if (!at91sam9x5_pmc)
163
return;
164
165
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
166
50000000);
167
if (IS_ERR(hw))
168
goto err_free;
169
170
bypass = of_property_read_bool(np, "atmel,osc-bypass");
171
172
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
173
bypass);
174
if (IS_ERR(hw))
175
goto err_free;
176
177
parent_names[0] = "main_rc_osc";
178
parent_names[1] = "main_osc";
179
hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
180
if (IS_ERR(hw))
181
goto err_free;
182
183
at91sam9x5_pmc->chws[PMC_MAIN] = hw;
184
185
hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
186
&at91rm9200_pll_layout, &plla_characteristics);
187
if (IS_ERR(hw))
188
goto err_free;
189
190
hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
191
if (IS_ERR(hw))
192
goto err_free;
193
194
at91sam9x5_pmc->chws[PMC_PLLACK] = hw;
195
196
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck", NULL);
197
if (IS_ERR(hw))
198
goto err_free;
199
200
at91sam9x5_pmc->chws[PMC_UTMI] = hw;
201
202
parent_names[0] = slck_name;
203
parent_names[1] = "mainck";
204
parent_names[2] = "plladivck";
205
parent_names[3] = "utmick";
206
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
207
parent_names, NULL,
208
&at91sam9x5_master_layout,
209
&mck_characteristics, &mck_lock);
210
if (IS_ERR(hw))
211
goto err_free;
212
213
hw = at91_clk_register_master_div(regmap, "masterck_div",
214
"masterck_pres", NULL,
215
&at91sam9x5_master_layout,
216
&mck_characteristics, &mck_lock,
217
CLK_SET_RATE_GATE, 0);
218
if (IS_ERR(hw))
219
goto err_free;
220
221
at91sam9x5_pmc->chws[PMC_MCK] = hw;
222
223
parent_names[0] = "plladivck";
224
parent_names[1] = "utmick";
225
hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
226
if (IS_ERR(hw))
227
goto err_free;
228
229
hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2);
230
if (IS_ERR(hw))
231
goto err_free;
232
233
parent_names[0] = slck_name;
234
parent_names[1] = "mainck";
235
parent_names[2] = "plladivck";
236
parent_names[3] = "utmick";
237
parent_names[4] = "masterck_div";
238
for (i = 0; i < 2; i++) {
239
char name[6];
240
241
snprintf(name, sizeof(name), "prog%d", i);
242
243
hw = at91_clk_register_programmable(regmap, name,
244
parent_names, NULL, 5, i,
245
&at91sam9x5_programmable_layout,
246
NULL);
247
if (IS_ERR(hw))
248
goto err_free;
249
250
at91sam9x5_pmc->pchws[i] = hw;
251
}
252
253
for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) {
254
hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n,
255
at91sam9x5_systemck[i].p, NULL,
256
at91sam9x5_systemck[i].id,
257
at91sam9x5_systemck[i].flags);
258
if (IS_ERR(hw))
259
goto err_free;
260
261
at91sam9x5_pmc->shws[at91sam9x5_systemck[i].id] = hw;
262
}
263
264
if (has_lcdck) {
265
hw = at91_clk_register_system(regmap, "lcdck", "masterck_div",
266
NULL, 3, 0);
267
if (IS_ERR(hw))
268
goto err_free;
269
270
at91sam9x5_pmc->shws[3] = hw;
271
}
272
273
for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) {
274
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
275
&at91sam9x5_pcr_layout,
276
at91sam9x5_periphck[i].n,
277
"masterck_div", NULL,
278
at91sam9x5_periphck[i].id,
279
&range, INT_MIN, 0);
280
if (IS_ERR(hw))
281
goto err_free;
282
283
at91sam9x5_pmc->phws[at91sam9x5_periphck[i].id] = hw;
284
}
285
286
for (i = 0; extra_pcks[i].id; i++) {
287
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
288
&at91sam9x5_pcr_layout,
289
extra_pcks[i].n,
290
"masterck_div", NULL,
291
extra_pcks[i].id,
292
&range, INT_MIN, 0);
293
if (IS_ERR(hw))
294
goto err_free;
295
296
at91sam9x5_pmc->phws[extra_pcks[i].id] = hw;
297
}
298
299
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc);
300
301
return;
302
303
err_free:
304
kfree(at91sam9x5_pmc);
305
}
306
307
static void __init at91sam9g15_pmc_setup(struct device_node *np)
308
{
309
at91sam9x5_pmc_setup(np, at91sam9g15_periphck, true);
310
}
311
312
CLK_OF_DECLARE(at91sam9g15_pmc, "atmel,at91sam9g15-pmc", at91sam9g15_pmc_setup);
313
314
static void __init at91sam9g25_pmc_setup(struct device_node *np)
315
{
316
at91sam9x5_pmc_setup(np, at91sam9g25_periphck, false);
317
}
318
319
CLK_OF_DECLARE(at91sam9g25_pmc, "atmel,at91sam9g25-pmc", at91sam9g25_pmc_setup);
320
321
static void __init at91sam9g35_pmc_setup(struct device_node *np)
322
{
323
at91sam9x5_pmc_setup(np, at91sam9g35_periphck, true);
324
}
325
326
CLK_OF_DECLARE(at91sam9g35_pmc, "atmel,at91sam9g35-pmc", at91sam9g35_pmc_setup);
327
328
static void __init at91sam9x25_pmc_setup(struct device_node *np)
329
{
330
at91sam9x5_pmc_setup(np, at91sam9x25_periphck, false);
331
}
332
333
CLK_OF_DECLARE(at91sam9x25_pmc, "atmel,at91sam9x25-pmc", at91sam9x25_pmc_setup);
334
335
static void __init at91sam9x35_pmc_setup(struct device_node *np)
336
{
337
at91sam9x5_pmc_setup(np, at91sam9x35_periphck, true);
338
}
339
340
CLK_OF_DECLARE(at91sam9x35_pmc, "atmel,at91sam9x35-pmc", at91sam9x35_pmc_setup);
341
342