diff options
author | Alexandre Belloni <alexandre.belloni@bootlin.com> | 2018-10-16 10:21:51 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2018-10-17 13:45:16 -0400 |
commit | 1eabdc2f9dd8f1bca1b985fd2b1243be836b30ad (patch) | |
tree | d90a834d730667e8518d090064a7e63492236938 | |
parent | c8923236a2894cc4b27010639e98e1a3086c0987 (diff) |
clk: at91: add at91sam9x5 PMCs driver
Add a driver for the PMC clocks of the at91sam9x5 SoCs
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 | 2 | ||||
-rw-r--r-- | drivers/clk/at91/at91sam9x5.c | 309 |
2 files changed, 310 insertions, 1 deletions
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 08399003c752..78dc71d653de 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile | |||
@@ -14,6 +14,6 @@ obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o | |||
14 | obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o | 14 | 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_AT91SAM9) += at91sam9260.o | 17 | obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9x5.o |
18 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o | 18 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o |
19 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o | 19 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o |
diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c new file mode 100644 index 000000000000..2fe225a697df --- /dev/null +++ b/drivers/clk/at91/at91sam9x5.c | |||
@@ -0,0 +1,309 @@ | |||
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 = 0, .max = 133333333 }, | ||
12 | .divisors = { 1, 2, 4, 3 }, | ||
13 | .have_div3_pres = 1, | ||
14 | }; | ||
15 | |||
16 | static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; | ||
17 | |||
18 | static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; | ||
19 | |||
20 | static struct clk_range plla_outputs[] = { | ||
21 | { .min = 745000000, .max = 800000000 }, | ||
22 | { .min = 695000000, .max = 750000000 }, | ||
23 | { .min = 645000000, .max = 700000000 }, | ||
24 | { .min = 595000000, .max = 650000000 }, | ||
25 | { .min = 545000000, .max = 600000000 }, | ||
26 | { .min = 495000000, .max = 555000000 }, | ||
27 | { .min = 445000000, .max = 500000000 }, | ||
28 | { .min = 400000000, .max = 450000000 }, | ||
29 | }; | ||
30 | |||
31 | static const struct clk_pll_characteristics plla_characteristics = { | ||
32 | .input = { .min = 2000000, .max = 32000000 }, | ||
33 | .num_output = ARRAY_SIZE(plla_outputs), | ||
34 | .output = plla_outputs, | ||
35 | .icpll = plla_icpll, | ||
36 | .out = plla_out, | ||
37 | }; | ||
38 | |||
39 | static const struct { | ||
40 | char *n; | ||
41 | char *p; | ||
42 | u8 id; | ||
43 | } at91sam9x5_systemck[] = { | ||
44 | { .n = "ddrck", .p = "masterck", .id = 2 }, | ||
45 | { .n = "smdck", .p = "smdclk", .id = 4 }, | ||
46 | { .n = "uhpck", .p = "usbck", .id = 6 }, | ||
47 | { .n = "udpck", .p = "usbck", .id = 7 }, | ||
48 | { .n = "pck0", .p = "prog0", .id = 8 }, | ||
49 | { .n = "pck1", .p = "prog1", .id = 9 }, | ||
50 | }; | ||
51 | |||
52 | struct pck { | ||
53 | char *n; | ||
54 | u8 id; | ||
55 | }; | ||
56 | |||
57 | static const struct pck at91sam9x5_periphck[] = { | ||
58 | { .n = "pioAB_clk", .id = 2, }, | ||
59 | { .n = "pioCD_clk", .id = 3, }, | ||
60 | { .n = "smd_clk", .id = 4, }, | ||
61 | { .n = "usart0_clk", .id = 5, }, | ||
62 | { .n = "usart1_clk", .id = 6, }, | ||
63 | { .n = "usart2_clk", .id = 7, }, | ||
64 | { .n = "twi0_clk", .id = 9, }, | ||
65 | { .n = "twi1_clk", .id = 10, }, | ||
66 | { .n = "twi2_clk", .id = 11, }, | ||
67 | { .n = "mci0_clk", .id = 12, }, | ||
68 | { .n = "spi0_clk", .id = 13, }, | ||
69 | { .n = "spi1_clk", .id = 14, }, | ||
70 | { .n = "uart0_clk", .id = 15, }, | ||
71 | { .n = "uart1_clk", .id = 16, }, | ||
72 | { .n = "tcb0_clk", .id = 17, }, | ||
73 | { .n = "pwm_clk", .id = 18, }, | ||
74 | { .n = "adc_clk", .id = 19, }, | ||
75 | { .n = "dma0_clk", .id = 20, }, | ||
76 | { .n = "dma1_clk", .id = 21, }, | ||
77 | { .n = "uhphs_clk", .id = 22, }, | ||
78 | { .n = "udphs_clk", .id = 23, }, | ||
79 | { .n = "mci1_clk", .id = 26, }, | ||
80 | { .n = "ssc0_clk", .id = 28, }, | ||
81 | }; | ||
82 | |||
83 | static const struct pck at91sam9g15_periphck[] = { | ||
84 | { .n = "lcdc_clk", .id = 25, }, | ||
85 | { /* sentinel */} | ||
86 | }; | ||
87 | |||
88 | static const struct pck at91sam9g25_periphck[] = { | ||
89 | { .n = "usart3_clk", .id = 8, }, | ||
90 | { .n = "macb0_clk", .id = 24, }, | ||
91 | { .n = "isi_clk", .id = 25, }, | ||
92 | { /* sentinel */} | ||
93 | }; | ||
94 | |||
95 | static const struct pck at91sam9g35_periphck[] = { | ||
96 | { .n = "macb0_clk", .id = 24, }, | ||
97 | { .n = "lcdc_clk", .id = 25, }, | ||
98 | { /* sentinel */} | ||
99 | }; | ||
100 | |||
101 | static const struct pck at91sam9x25_periphck[] = { | ||
102 | { .n = "usart3_clk", .id = 8, }, | ||
103 | { .n = "macb0_clk", .id = 24, }, | ||
104 | { .n = "macb1_clk", .id = 27, }, | ||
105 | { .n = "can0_clk", .id = 29, }, | ||
106 | { .n = "can1_clk", .id = 30, }, | ||
107 | { /* sentinel */} | ||
108 | }; | ||
109 | |||
110 | static const struct pck at91sam9x35_periphck[] = { | ||
111 | { .n = "macb0_clk", .id = 24, }, | ||
112 | { .n = "lcdc_clk", .id = 25, }, | ||
113 | { .n = "can0_clk", .id = 29, }, | ||
114 | { .n = "can1_clk", .id = 30, }, | ||
115 | { /* sentinel */} | ||
116 | }; | ||
117 | |||
118 | static void __init at91sam9x5_pmc_setup(struct device_node *np, | ||
119 | const struct pck *extra_pcks, | ||
120 | bool has_lcdck) | ||
121 | { | ||
122 | struct clk_range range = CLK_RANGE(0, 0); | ||
123 | const char *slck_name, *mainxtal_name; | ||
124 | struct pmc_data *at91sam9x5_pmc; | ||
125 | const char *parent_names[6]; | ||
126 | struct regmap *regmap; | ||
127 | struct clk_hw *hw; | ||
128 | int i; | ||
129 | bool bypass; | ||
130 | |||
131 | i = of_property_match_string(np, "clock-names", "slow_clk"); | ||
132 | if (i < 0) | ||
133 | return; | ||
134 | |||
135 | slck_name = of_clk_get_parent_name(np, i); | ||
136 | |||
137 | i = of_property_match_string(np, "clock-names", "main_xtal"); | ||
138 | if (i < 0) | ||
139 | return; | ||
140 | mainxtal_name = of_clk_get_parent_name(np, i); | ||
141 | |||
142 | regmap = syscon_node_to_regmap(np); | ||
143 | if (IS_ERR(regmap)) | ||
144 | return; | ||
145 | |||
146 | at91sam9x5_pmc = pmc_data_allocate(PMC_MAIN + 1, | ||
147 | nck(at91sam9x5_systemck), | ||
148 | nck(at91sam9x35_periphck), 0); | ||
149 | if (!at91sam9x5_pmc) | ||
150 | return; | ||
151 | |||
152 | hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, | ||
153 | 50000000); | ||
154 | if (IS_ERR(hw)) | ||
155 | goto err_free; | ||
156 | |||
157 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
158 | |||
159 | hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, | ||
160 | bypass); | ||
161 | if (IS_ERR(hw)) | ||
162 | goto err_free; | ||
163 | |||
164 | parent_names[0] = "main_rc_osc"; | ||
165 | parent_names[1] = "main_osc"; | ||
166 | hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); | ||
167 | if (IS_ERR(hw)) | ||
168 | goto err_free; | ||
169 | |||
170 | at91sam9x5_pmc->chws[PMC_MAIN] = hw; | ||
171 | |||
172 | hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0, | ||
173 | &at91rm9200_pll_layout, &plla_characteristics); | ||
174 | if (IS_ERR(hw)) | ||
175 | goto err_free; | ||
176 | |||
177 | hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack"); | ||
178 | if (IS_ERR(hw)) | ||
179 | goto err_free; | ||
180 | |||
181 | hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck"); | ||
182 | if (IS_ERR(hw)) | ||
183 | goto err_free; | ||
184 | |||
185 | at91sam9x5_pmc->chws[PMC_UTMI] = hw; | ||
186 | |||
187 | parent_names[0] = slck_name; | ||
188 | parent_names[1] = "mainck"; | ||
189 | parent_names[2] = "plladivck"; | ||
190 | parent_names[3] = "utmick"; | ||
191 | hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, | ||
192 | &at91sam9x5_master_layout, | ||
193 | &mck_characteristics); | ||
194 | if (IS_ERR(hw)) | ||
195 | goto err_free; | ||
196 | |||
197 | at91sam9x5_pmc->chws[PMC_MCK] = hw; | ||
198 | |||
199 | parent_names[0] = "plladivck"; | ||
200 | parent_names[1] = "utmick"; | ||
201 | hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2); | ||
202 | if (IS_ERR(hw)) | ||
203 | goto err_free; | ||
204 | |||
205 | hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2); | ||
206 | if (IS_ERR(hw)) | ||
207 | goto err_free; | ||
208 | |||
209 | parent_names[0] = slck_name; | ||
210 | parent_names[1] = "mainck"; | ||
211 | parent_names[2] = "plladivck"; | ||
212 | parent_names[3] = "utmick"; | ||
213 | parent_names[4] = "mck"; | ||
214 | for (i = 0; i < 2; i++) { | ||
215 | char name[6]; | ||
216 | |||
217 | snprintf(name, sizeof(name), "prog%d", i); | ||
218 | |||
219 | hw = at91_clk_register_programmable(regmap, name, | ||
220 | parent_names, 5, i, | ||
221 | &at91sam9x5_programmable_layout); | ||
222 | if (IS_ERR(hw)) | ||
223 | goto err_free; | ||
224 | } | ||
225 | |||
226 | for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) { | ||
227 | hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n, | ||
228 | at91sam9x5_systemck[i].p, | ||
229 | at91sam9x5_systemck[i].id); | ||
230 | if (IS_ERR(hw)) | ||
231 | goto err_free; | ||
232 | |||
233 | at91sam9x5_pmc->shws[at91sam9x5_systemck[i].id] = hw; | ||
234 | } | ||
235 | |||
236 | if (has_lcdck) { | ||
237 | hw = at91_clk_register_system(regmap, "lcdck", "masterck", 3); | ||
238 | if (IS_ERR(hw)) | ||
239 | goto err_free; | ||
240 | |||
241 | at91sam9x5_pmc->shws[3] = hw; | ||
242 | } | ||
243 | |||
244 | for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) { | ||
245 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | ||
246 | at91sam9x5_periphck[i].n, | ||
247 | "masterck", | ||
248 | at91sam9x5_periphck[i].id, | ||
249 | &range); | ||
250 | if (IS_ERR(hw)) | ||
251 | goto err_free; | ||
252 | |||
253 | at91sam9x5_pmc->phws[at91sam9x5_periphck[i].id] = hw; | ||
254 | } | ||
255 | |||
256 | for (i = 0; extra_pcks[i].id; i++) { | ||
257 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | ||
258 | extra_pcks[i].n, | ||
259 | "masterck", | ||
260 | extra_pcks[i].id, | ||
261 | &range); | ||
262 | if (IS_ERR(hw)) | ||
263 | goto err_free; | ||
264 | |||
265 | at91sam9x5_pmc->phws[extra_pcks[i].id] = hw; | ||
266 | } | ||
267 | |||
268 | of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc); | ||
269 | |||
270 | return; | ||
271 | |||
272 | err_free: | ||
273 | pmc_data_free(at91sam9x5_pmc); | ||
274 | } | ||
275 | |||
276 | static void __init at91sam9g15_pmc_setup(struct device_node *np) | ||
277 | { | ||
278 | at91sam9x5_pmc_setup(np, at91sam9g15_periphck, true); | ||
279 | } | ||
280 | CLK_OF_DECLARE_DRIVER(at91sam9g15_pmc, "atmel,at91sam9g15-pmc", | ||
281 | at91sam9g15_pmc_setup); | ||
282 | |||
283 | static void __init at91sam9g25_pmc_setup(struct device_node *np) | ||
284 | { | ||
285 | at91sam9x5_pmc_setup(np, at91sam9g25_periphck, false); | ||
286 | } | ||
287 | CLK_OF_DECLARE_DRIVER(at91sam9g25_pmc, "atmel,at91sam9g25-pmc", | ||
288 | at91sam9g25_pmc_setup); | ||
289 | |||
290 | static void __init at91sam9g35_pmc_setup(struct device_node *np) | ||
291 | { | ||
292 | at91sam9x5_pmc_setup(np, at91sam9g35_periphck, true); | ||
293 | } | ||
294 | CLK_OF_DECLARE_DRIVER(at91sam9g35_pmc, "atmel,at91sam9g35-pmc", | ||
295 | at91sam9g35_pmc_setup); | ||
296 | |||
297 | static void __init at91sam9x25_pmc_setup(struct device_node *np) | ||
298 | { | ||
299 | at91sam9x5_pmc_setup(np, at91sam9x25_periphck, false); | ||
300 | } | ||
301 | CLK_OF_DECLARE_DRIVER(at91sam9x25_pmc, "atmel,at91sam9x25-pmc", | ||
302 | at91sam9x25_pmc_setup); | ||
303 | |||
304 | static void __init at91sam9x35_pmc_setup(struct device_node *np) | ||
305 | { | ||
306 | at91sam9x5_pmc_setup(np, at91sam9x35_periphck, true); | ||
307 | } | ||
308 | CLK_OF_DECLARE_DRIVER(at91sam9x35_pmc, "atmel,at91sam9x35-pmc", | ||
309 | at91sam9x35_pmc_setup); | ||