diff options
author | Alexandre Belloni <alexandre.belloni@bootlin.com> | 2018-10-16 10:21:49 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2018-10-17 13:44:53 -0400 |
commit | a2038077de9a9e35f14a72612e2885193d4490d4 (patch) | |
tree | c2349503f744d0c5270c22205ffbb43f3dab9eb0 | |
parent | 084b696bb509d5943d94e282a4e349426ac85dc6 (diff) |
clk: at91: add sama5d2 PMC driver
Add a driver for the PMC clocks of the sama5d2
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
[sboyd@kernel.org: Make i signed to fix signedness bug]
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r-- | drivers/clk/at91/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/at91/sama5d2.c | 336 |
2 files changed, 337 insertions, 0 deletions
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 7cdb762f3e2e..1e7c70dda723 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile | |||
@@ -15,3 +15,4 @@ obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o | |||
15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o | 15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o |
16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o | 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o |
17 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o | 17 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o |
18 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o | ||
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c new file mode 100644 index 000000000000..d69ad96fe988 --- /dev/null +++ b/drivers/clk/at91/sama5d2.c | |||
@@ -0,0 +1,336 @@ | |||
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 const struct clk_master_characteristics mck_characteristics = { | ||
11 | .output = { .min = 124000000, .max = 166000000 }, | ||
12 | .divisors = { 1, 2, 4, 3 }, | ||
13 | }; | ||
14 | |||
15 | static u8 plla_out[] = { 0 }; | ||
16 | |||
17 | static u16 plla_icpll[] = { 0 }; | ||
18 | |||
19 | static struct clk_range plla_outputs[] = { | ||
20 | { .min = 600000000, .max = 1200000000 }, | ||
21 | }; | ||
22 | |||
23 | static const struct clk_pll_characteristics plla_characteristics = { | ||
24 | .input = { .min = 12000000, .max = 12000000 }, | ||
25 | .num_output = ARRAY_SIZE(plla_outputs), | ||
26 | .output = plla_outputs, | ||
27 | .icpll = plla_icpll, | ||
28 | .out = plla_out, | ||
29 | }; | ||
30 | |||
31 | static const struct { | ||
32 | char *n; | ||
33 | char *p; | ||
34 | u8 id; | ||
35 | } sama5d2_systemck[] = { | ||
36 | { .n = "ddrck", .p = "masterck", .id = 2 }, | ||
37 | { .n = "lcdck", .p = "masterck", .id = 3 }, | ||
38 | { .n = "uhpck", .p = "usbck", .id = 6 }, | ||
39 | { .n = "udpck", .p = "usbck", .id = 7 }, | ||
40 | { .n = "pck0", .p = "prog0", .id = 8 }, | ||
41 | { .n = "pck1", .p = "prog1", .id = 9 }, | ||
42 | { .n = "pck2", .p = "prog2", .id = 10 }, | ||
43 | { .n = "iscck", .p = "masterck", .id = 18 }, | ||
44 | }; | ||
45 | |||
46 | static const struct { | ||
47 | char *n; | ||
48 | u8 id; | ||
49 | struct clk_range r; | ||
50 | } sama5d2_periph32ck[] = { | ||
51 | { .n = "macb0_clk", .id = 5, .r = { .min = 0, .max = 83000000 }, }, | ||
52 | { .n = "tdes_clk", .id = 11, .r = { .min = 0, .max = 83000000 }, }, | ||
53 | { .n = "matrix1_clk", .id = 14, }, | ||
54 | { .n = "hsmc_clk", .id = 17, }, | ||
55 | { .n = "pioA_clk", .id = 18, .r = { .min = 0, .max = 83000000 }, }, | ||
56 | { .n = "flx0_clk", .id = 19, .r = { .min = 0, .max = 83000000 }, }, | ||
57 | { .n = "flx1_clk", .id = 20, .r = { .min = 0, .max = 83000000 }, }, | ||
58 | { .n = "flx2_clk", .id = 21, .r = { .min = 0, .max = 83000000 }, }, | ||
59 | { .n = "flx3_clk", .id = 22, .r = { .min = 0, .max = 83000000 }, }, | ||
60 | { .n = "flx4_clk", .id = 23, .r = { .min = 0, .max = 83000000 }, }, | ||
61 | { .n = "uart0_clk", .id = 24, .r = { .min = 0, .max = 83000000 }, }, | ||
62 | { .n = "uart1_clk", .id = 25, .r = { .min = 0, .max = 83000000 }, }, | ||
63 | { .n = "uart2_clk", .id = 26, .r = { .min = 0, .max = 83000000 }, }, | ||
64 | { .n = "uart3_clk", .id = 27, .r = { .min = 0, .max = 83000000 }, }, | ||
65 | { .n = "uart4_clk", .id = 28, .r = { .min = 0, .max = 83000000 }, }, | ||
66 | { .n = "twi0_clk", .id = 29, .r = { .min = 0, .max = 83000000 }, }, | ||
67 | { .n = "twi1_clk", .id = 30, .r = { .min = 0, .max = 83000000 }, }, | ||
68 | { .n = "spi0_clk", .id = 33, .r = { .min = 0, .max = 83000000 }, }, | ||
69 | { .n = "spi1_clk", .id = 34, .r = { .min = 0, .max = 83000000 }, }, | ||
70 | { .n = "tcb0_clk", .id = 35, .r = { .min = 0, .max = 83000000 }, }, | ||
71 | { .n = "tcb1_clk", .id = 36, .r = { .min = 0, .max = 83000000 }, }, | ||
72 | { .n = "pwm_clk", .id = 38, .r = { .min = 0, .max = 83000000 }, }, | ||
73 | { .n = "adc_clk", .id = 40, .r = { .min = 0, .max = 83000000 }, }, | ||
74 | { .n = "uhphs_clk", .id = 41, .r = { .min = 0, .max = 83000000 }, }, | ||
75 | { .n = "udphs_clk", .id = 42, .r = { .min = 0, .max = 83000000 }, }, | ||
76 | { .n = "ssc0_clk", .id = 43, .r = { .min = 0, .max = 83000000 }, }, | ||
77 | { .n = "ssc1_clk", .id = 44, .r = { .min = 0, .max = 83000000 }, }, | ||
78 | { .n = "trng_clk", .id = 47, .r = { .min = 0, .max = 83000000 }, }, | ||
79 | { .n = "pdmic_clk", .id = 48, .r = { .min = 0, .max = 83000000 }, }, | ||
80 | { .n = "securam_clk", .id = 51, }, | ||
81 | { .n = "i2s0_clk", .id = 54, .r = { .min = 0, .max = 83000000 }, }, | ||
82 | { .n = "i2s1_clk", .id = 55, .r = { .min = 0, .max = 83000000 }, }, | ||
83 | { .n = "can0_clk", .id = 56, .r = { .min = 0, .max = 83000000 }, }, | ||
84 | { .n = "can1_clk", .id = 57, .r = { .min = 0, .max = 83000000 }, }, | ||
85 | { .n = "classd_clk", .id = 59, .r = { .min = 0, .max = 83000000 }, }, | ||
86 | }; | ||
87 | |||
88 | static const struct { | ||
89 | char *n; | ||
90 | u8 id; | ||
91 | } sama5d2_periphck[] = { | ||
92 | { .n = "dma0_clk", .id = 6, }, | ||
93 | { .n = "dma1_clk", .id = 7, }, | ||
94 | { .n = "aes_clk", .id = 9, }, | ||
95 | { .n = "aesb_clk", .id = 10, }, | ||
96 | { .n = "sha_clk", .id = 12, }, | ||
97 | { .n = "mpddr_clk", .id = 13, }, | ||
98 | { .n = "matrix0_clk", .id = 15, }, | ||
99 | { .n = "sdmmc0_hclk", .id = 31, }, | ||
100 | { .n = "sdmmc1_hclk", .id = 32, }, | ||
101 | { .n = "lcdc_clk", .id = 45, }, | ||
102 | { .n = "isc_clk", .id = 46, }, | ||
103 | { .n = "qspi0_clk", .id = 52, }, | ||
104 | { .n = "qspi1_clk", .id = 53, }, | ||
105 | }; | ||
106 | |||
107 | static const struct { | ||
108 | char *n; | ||
109 | u8 id; | ||
110 | struct clk_range r; | ||
111 | bool pll; | ||
112 | } sama5d2_gck[] = { | ||
113 | { .n = "sdmmc0_gclk", .id = 31, }, | ||
114 | { .n = "sdmmc1_gclk", .id = 32, }, | ||
115 | { .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, }, | ||
116 | { .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, }, | ||
117 | { .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, }, | ||
118 | { .n = "isc_gclk", .id = 46, }, | ||
119 | { .n = "pdmic_gclk", .id = 48, }, | ||
120 | { .n = "i2s0_gclk", .id = 54, .pll = true }, | ||
121 | { .n = "i2s1_gclk", .id = 55, .pll = true }, | ||
122 | { .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, }, | ||
123 | { .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, }, | ||
124 | { .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 }, | ||
125 | .pll = true }, | ||
126 | }; | ||
127 | |||
128 | static void __init sama5d2_pmc_setup(struct device_node *np) | ||
129 | { | ||
130 | struct clk_range range = CLK_RANGE(0, 0); | ||
131 | const char *slck_name, *mainxtal_name; | ||
132 | struct pmc_data *sama5d2_pmc; | ||
133 | const char *parent_names[6]; | ||
134 | struct regmap *regmap, *regmap_sfr; | ||
135 | struct clk_hw *hw; | ||
136 | int i; | ||
137 | bool bypass; | ||
138 | |||
139 | i = of_property_match_string(np, "clock-names", "slow_clk"); | ||
140 | if (i < 0) | ||
141 | return; | ||
142 | |||
143 | slck_name = of_clk_get_parent_name(np, i); | ||
144 | |||
145 | i = of_property_match_string(np, "clock-names", "main_xtal"); | ||
146 | if (i < 0) | ||
147 | return; | ||
148 | mainxtal_name = of_clk_get_parent_name(np, i); | ||
149 | |||
150 | regmap = syscon_node_to_regmap(np); | ||
151 | if (IS_ERR(regmap)) | ||
152 | return; | ||
153 | |||
154 | sama5d2_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1, | ||
155 | nck(sama5d2_systemck), | ||
156 | nck(sama5d2_periph32ck), | ||
157 | nck(sama5d2_gck)); | ||
158 | if (!sama5d2_pmc) | ||
159 | return; | ||
160 | |||
161 | hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, | ||
162 | 100000000); | ||
163 | if (IS_ERR(hw)) | ||
164 | goto err_free; | ||
165 | |||
166 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
167 | |||
168 | hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, | ||
169 | bypass); | ||
170 | if (IS_ERR(hw)) | ||
171 | goto err_free; | ||
172 | |||
173 | parent_names[0] = "main_rc_osc"; | ||
174 | parent_names[1] = "main_osc"; | ||
175 | hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); | ||
176 | if (IS_ERR(hw)) | ||
177 | goto err_free; | ||
178 | |||
179 | sama5d2_pmc->chws[PMC_MAIN] = hw; | ||
180 | |||
181 | hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0, | ||
182 | &sama5d3_pll_layout, &plla_characteristics); | ||
183 | if (IS_ERR(hw)) | ||
184 | goto err_free; | ||
185 | |||
186 | hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack"); | ||
187 | if (IS_ERR(hw)) | ||
188 | goto err_free; | ||
189 | |||
190 | hw = at91_clk_register_audio_pll_frac(regmap, "audiopll_fracck", | ||
191 | "mainck"); | ||
192 | if (IS_ERR(hw)) | ||
193 | goto err_free; | ||
194 | |||
195 | hw = at91_clk_register_audio_pll_pad(regmap, "audiopll_padck", | ||
196 | "audiopll_fracck"); | ||
197 | if (IS_ERR(hw)) | ||
198 | goto err_free; | ||
199 | |||
200 | hw = at91_clk_register_audio_pll_pmc(regmap, "audiopll_pmcck", | ||
201 | "audiopll_fracck"); | ||
202 | if (IS_ERR(hw)) | ||
203 | goto err_free; | ||
204 | |||
205 | regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); | ||
206 | if (IS_ERR(regmap_sfr)) | ||
207 | regmap_sfr = NULL; | ||
208 | |||
209 | hw = at91_clk_register_utmi(regmap, regmap_sfr, "utmick", "mainck"); | ||
210 | if (IS_ERR(hw)) | ||
211 | goto err_free; | ||
212 | |||
213 | sama5d2_pmc->chws[PMC_UTMI] = hw; | ||
214 | |||
215 | parent_names[0] = slck_name; | ||
216 | parent_names[1] = "mainck"; | ||
217 | parent_names[2] = "plladivck"; | ||
218 | parent_names[3] = "utmick"; | ||
219 | hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, | ||
220 | &at91sam9x5_master_layout, | ||
221 | &mck_characteristics); | ||
222 | if (IS_ERR(hw)) | ||
223 | goto err_free; | ||
224 | |||
225 | sama5d2_pmc->chws[PMC_MCK] = hw; | ||
226 | |||
227 | hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck"); | ||
228 | if (IS_ERR(hw)) | ||
229 | goto err_free; | ||
230 | |||
231 | sama5d2_pmc->chws[PMC_MCK2] = hw; | ||
232 | |||
233 | parent_names[0] = "plladivck"; | ||
234 | parent_names[1] = "utmick"; | ||
235 | hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2); | ||
236 | if (IS_ERR(hw)) | ||
237 | goto err_free; | ||
238 | |||
239 | parent_names[0] = slck_name; | ||
240 | parent_names[1] = "mainck"; | ||
241 | parent_names[2] = "plladivck"; | ||
242 | parent_names[3] = "utmick"; | ||
243 | parent_names[4] = "mck"; | ||
244 | for (i = 0; i < 3; i++) { | ||
245 | char name[6]; | ||
246 | |||
247 | snprintf(name, sizeof(name), "prog%d", i); | ||
248 | |||
249 | hw = at91_clk_register_programmable(regmap, name, | ||
250 | parent_names, 5, i, | ||
251 | &at91sam9x5_programmable_layout); | ||
252 | if (IS_ERR(hw)) | ||
253 | goto err_free; | ||
254 | } | ||
255 | |||
256 | for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) { | ||
257 | hw = at91_clk_register_system(regmap, sama5d2_systemck[i].n, | ||
258 | sama5d2_systemck[i].p, | ||
259 | sama5d2_systemck[i].id); | ||
260 | if (IS_ERR(hw)) | ||
261 | goto err_free; | ||
262 | |||
263 | sama5d2_pmc->shws[sama5d2_systemck[i].id] = hw; | ||
264 | } | ||
265 | |||
266 | for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) { | ||
267 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | ||
268 | sama5d2_periphck[i].n, | ||
269 | "masterck", | ||
270 | sama5d2_periphck[i].id, | ||
271 | &range); | ||
272 | if (IS_ERR(hw)) | ||
273 | goto err_free; | ||
274 | |||
275 | sama5d2_pmc->phws[sama5d2_periphck[i].id] = hw; | ||
276 | } | ||
277 | |||
278 | for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) { | ||
279 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | ||
280 | sama5d2_periph32ck[i].n, | ||
281 | "h32mxck", | ||
282 | sama5d2_periph32ck[i].id, | ||
283 | &sama5d2_periph32ck[i].r); | ||
284 | if (IS_ERR(hw)) | ||
285 | goto err_free; | ||
286 | |||
287 | sama5d2_pmc->phws[sama5d2_periph32ck[i].id] = hw; | ||
288 | } | ||
289 | |||
290 | parent_names[0] = slck_name; | ||
291 | parent_names[1] = "mainck"; | ||
292 | parent_names[2] = "plladivck"; | ||
293 | parent_names[3] = "utmick"; | ||
294 | parent_names[4] = "mck"; | ||
295 | parent_names[5] = "audiopll_pmcck"; | ||
296 | for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) { | ||
297 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, | ||
298 | sama5d2_gck[i].n, | ||
299 | parent_names, 6, | ||
300 | sama5d2_gck[i].id, | ||
301 | sama5d2_gck[i].pll, | ||
302 | &sama5d2_gck[i].r); | ||
303 | if (IS_ERR(hw)) | ||
304 | goto err_free; | ||
305 | |||
306 | sama5d2_pmc->ghws[sama5d2_gck[i].id] = hw; | ||
307 | } | ||
308 | |||
309 | if (regmap_sfr) { | ||
310 | parent_names[0] = "i2s0_clk"; | ||
311 | parent_names[1] = "i2s0_gclk"; | ||
312 | hw = at91_clk_i2s_mux_register(regmap_sfr, "i2s0_muxclk", | ||
313 | parent_names, 2, 0); | ||
314 | if (IS_ERR(hw)) | ||
315 | goto err_free; | ||
316 | |||
317 | sama5d2_pmc->chws[PMC_I2S0_MUX] = hw; | ||
318 | |||
319 | parent_names[0] = "i2s1_clk"; | ||
320 | parent_names[1] = "i2s1_gclk"; | ||
321 | hw = at91_clk_i2s_mux_register(regmap_sfr, "i2s1_muxclk", | ||
322 | parent_names, 2, 1); | ||
323 | if (IS_ERR(hw)) | ||
324 | goto err_free; | ||
325 | |||
326 | sama5d2_pmc->chws[PMC_I2S1_MUX] = hw; | ||
327 | } | ||
328 | |||
329 | of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d2_pmc); | ||
330 | |||
331 | return; | ||
332 | |||
333 | err_free: | ||
334 | pmc_data_free(sama5d2_pmc); | ||
335 | } | ||
336 | CLK_OF_DECLARE_DRIVER(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup); | ||