1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Nuvoton NPCM7xx Clock Generator |
4 | * All the clocks are initialized by the bootloader, so this driver allow only |
5 | * reading of current settings directly from the hardware. |
6 | * |
7 | * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/io.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/of.h> |
15 | #include <linux/of_address.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/err.h> |
18 | #include <linux/bitfield.h> |
19 | |
20 | #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h> |
21 | |
22 | struct npcm7xx_clk_pll { |
23 | struct clk_hw hw; |
24 | void __iomem *pllcon; |
25 | u8 flags; |
26 | }; |
27 | |
28 | #define to_npcm7xx_clk_pll(_hw) container_of(_hw, struct npcm7xx_clk_pll, hw) |
29 | |
30 | #define PLLCON_LOKI BIT(31) |
31 | #define PLLCON_LOKS BIT(30) |
32 | #define PLLCON_FBDV GENMASK(27, 16) |
33 | #define PLLCON_OTDV2 GENMASK(15, 13) |
34 | #define PLLCON_PWDEN BIT(12) |
35 | #define PLLCON_OTDV1 GENMASK(10, 8) |
36 | #define PLLCON_INDV GENMASK(5, 0) |
37 | |
38 | static unsigned long npcm7xx_clk_pll_recalc_rate(struct clk_hw *hw, |
39 | unsigned long parent_rate) |
40 | { |
41 | struct npcm7xx_clk_pll *pll = to_npcm7xx_clk_pll(hw); |
42 | unsigned long fbdv, indv, otdv1, otdv2; |
43 | unsigned int val; |
44 | u64 ret; |
45 | |
46 | if (parent_rate == 0) { |
47 | pr_err("%s: parent rate is zero" , __func__); |
48 | return 0; |
49 | } |
50 | |
51 | val = readl_relaxed(pll->pllcon); |
52 | |
53 | indv = FIELD_GET(PLLCON_INDV, val); |
54 | fbdv = FIELD_GET(PLLCON_FBDV, val); |
55 | otdv1 = FIELD_GET(PLLCON_OTDV1, val); |
56 | otdv2 = FIELD_GET(PLLCON_OTDV2, val); |
57 | |
58 | ret = (u64)parent_rate * fbdv; |
59 | do_div(ret, indv * otdv1 * otdv2); |
60 | |
61 | return ret; |
62 | } |
63 | |
64 | static const struct clk_ops npcm7xx_clk_pll_ops = { |
65 | .recalc_rate = npcm7xx_clk_pll_recalc_rate, |
66 | }; |
67 | |
68 | static struct clk_hw * |
69 | npcm7xx_clk_register_pll(void __iomem *pllcon, const char *name, |
70 | const char *parent_name, unsigned long flags) |
71 | { |
72 | struct npcm7xx_clk_pll *pll; |
73 | struct clk_init_data init; |
74 | struct clk_hw *hw; |
75 | int ret; |
76 | |
77 | pll = kzalloc(size: sizeof(*pll), GFP_KERNEL); |
78 | if (!pll) |
79 | return ERR_PTR(error: -ENOMEM); |
80 | |
81 | pr_debug("%s reg, name=%s, p=%s\n" , __func__, name, parent_name); |
82 | |
83 | init.name = name; |
84 | init.ops = &npcm7xx_clk_pll_ops; |
85 | init.parent_names = &parent_name; |
86 | init.num_parents = 1; |
87 | init.flags = flags; |
88 | |
89 | pll->pllcon = pllcon; |
90 | pll->hw.init = &init; |
91 | |
92 | hw = &pll->hw; |
93 | |
94 | ret = clk_hw_register(NULL, hw); |
95 | if (ret) { |
96 | kfree(objp: pll); |
97 | hw = ERR_PTR(error: ret); |
98 | } |
99 | |
100 | return hw; |
101 | } |
102 | |
103 | #define NPCM7XX_CLKEN1 (0x00) |
104 | #define NPCM7XX_CLKEN2 (0x28) |
105 | #define NPCM7XX_CLKEN3 (0x30) |
106 | #define NPCM7XX_CLKSEL (0x04) |
107 | #define NPCM7XX_CLKDIV1 (0x08) |
108 | #define NPCM7XX_CLKDIV2 (0x2C) |
109 | #define NPCM7XX_CLKDIV3 (0x58) |
110 | #define NPCM7XX_PLLCON0 (0x0C) |
111 | #define NPCM7XX_PLLCON1 (0x10) |
112 | #define NPCM7XX_PLLCON2 (0x54) |
113 | #define NPCM7XX_SWRSTR (0x14) |
114 | #define NPCM7XX_IRQWAKECON (0x18) |
115 | #define NPCM7XX_IRQWAKEFLAG (0x1C) |
116 | #define NPCM7XX_IPSRST1 (0x20) |
117 | #define NPCM7XX_IPSRST2 (0x24) |
118 | #define NPCM7XX_IPSRST3 (0x34) |
119 | #define NPCM7XX_WD0RCR (0x38) |
120 | #define NPCM7XX_WD1RCR (0x3C) |
121 | #define NPCM7XX_WD2RCR (0x40) |
122 | #define NPCM7XX_SWRSTC1 (0x44) |
123 | #define NPCM7XX_SWRSTC2 (0x48) |
124 | #define NPCM7XX_SWRSTC3 (0x4C) |
125 | #define NPCM7XX_SWRSTC4 (0x50) |
126 | #define NPCM7XX_CORSTC (0x5C) |
127 | #define NPCM7XX_PLLCONG (0x60) |
128 | #define NPCM7XX_AHBCKFI (0x64) |
129 | #define NPCM7XX_SECCNT (0x68) |
130 | #define NPCM7XX_CNTR25M (0x6C) |
131 | |
132 | struct npcm7xx_clk_mux_data { |
133 | u8 shift; |
134 | u8 mask; |
135 | u32 *table; |
136 | const char *name; |
137 | const char * const *parent_names; |
138 | u8 num_parents; |
139 | unsigned long flags; |
140 | /* |
141 | * If this clock is exported via DT, set onecell_idx to constant |
142 | * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for |
143 | * this specific clock. Otherwise, set to -1. |
144 | */ |
145 | int onecell_idx; |
146 | |
147 | }; |
148 | |
149 | struct npcm7xx_clk_div_data { |
150 | u32 reg; |
151 | u8 shift; |
152 | u8 width; |
153 | const char *name; |
154 | const char *parent_name; |
155 | u8 clk_divider_flags; |
156 | unsigned long flags; |
157 | /* |
158 | * If this clock is exported via DT, set onecell_idx to constant |
159 | * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for |
160 | * this specific clock. Otherwise, set to -1. |
161 | */ |
162 | int onecell_idx; |
163 | }; |
164 | |
165 | struct npcm7xx_clk_pll_data { |
166 | u32 reg; |
167 | const char *name; |
168 | const char *parent_name; |
169 | unsigned long flags; |
170 | /* |
171 | * If this clock is exported via DT, set onecell_idx to constant |
172 | * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for |
173 | * this specific clock. Otherwise, set to -1. |
174 | */ |
175 | int onecell_idx; |
176 | }; |
177 | |
178 | /* |
179 | * Single copy of strings used to refer to clocks within this driver indexed by |
180 | * above enum. |
181 | */ |
182 | #define NPCM7XX_CLK_S_REFCLK "refclk" |
183 | #define NPCM7XX_CLK_S_SYSBYPCK "sysbypck" |
184 | #define NPCM7XX_CLK_S_MCBYPCK "mcbypck" |
185 | #define NPCM7XX_CLK_S_GFXBYPCK "gfxbypck" |
186 | #define NPCM7XX_CLK_S_PLL0 "pll0" |
187 | #define NPCM7XX_CLK_S_PLL1 "pll1" |
188 | #define NPCM7XX_CLK_S_PLL1_DIV2 "pll1_div2" |
189 | #define NPCM7XX_CLK_S_PLL2 "pll2" |
190 | #define NPCM7XX_CLK_S_PLL_GFX "pll_gfx" |
191 | #define NPCM7XX_CLK_S_PLL2_DIV2 "pll2_div2" |
192 | #define NPCM7XX_CLK_S_PIX_MUX "gfx_pixel" |
193 | #define NPCM7XX_CLK_S_GPRFSEL_MUX "gprfsel_mux" |
194 | #define NPCM7XX_CLK_S_MC_MUX "mc_phy" |
195 | #define NPCM7XX_CLK_S_CPU_MUX "cpu" /*AKA system clock.*/ |
196 | #define NPCM7XX_CLK_S_MC "mc" |
197 | #define NPCM7XX_CLK_S_AXI "axi" /*AKA CLK2*/ |
198 | #define NPCM7XX_CLK_S_AHB "ahb" /*AKA CLK4*/ |
199 | #define NPCM7XX_CLK_S_CLKOUT_MUX "clkout_mux" |
200 | #define NPCM7XX_CLK_S_UART_MUX "uart_mux" |
201 | #define NPCM7XX_CLK_S_TIM_MUX "timer_mux" |
202 | #define NPCM7XX_CLK_S_SD_MUX "sd_mux" |
203 | #define NPCM7XX_CLK_S_GFXM_MUX "gfxm_mux" |
204 | #define NPCM7XX_CLK_S_SU_MUX "serial_usb_mux" |
205 | #define NPCM7XX_CLK_S_DVC_MUX "dvc_mux" |
206 | #define NPCM7XX_CLK_S_GFX_MUX "gfx_mux" |
207 | #define NPCM7XX_CLK_S_GFX_PIXEL "gfx_pixel" |
208 | #define NPCM7XX_CLK_S_SPI0 "spi0" |
209 | #define NPCM7XX_CLK_S_SPI3 "spi3" |
210 | #define NPCM7XX_CLK_S_SPIX "spix" |
211 | #define NPCM7XX_CLK_S_APB1 "apb1" |
212 | #define NPCM7XX_CLK_S_APB2 "apb2" |
213 | #define NPCM7XX_CLK_S_APB3 "apb3" |
214 | #define NPCM7XX_CLK_S_APB4 "apb4" |
215 | #define NPCM7XX_CLK_S_APB5 "apb5" |
216 | #define NPCM7XX_CLK_S_TOCK "tock" |
217 | #define NPCM7XX_CLK_S_CLKOUT "clkout" |
218 | #define NPCM7XX_CLK_S_UART "uart" |
219 | #define NPCM7XX_CLK_S_TIMER "timer" |
220 | #define NPCM7XX_CLK_S_MMC "mmc" |
221 | #define NPCM7XX_CLK_S_SDHC "sdhc" |
222 | #define NPCM7XX_CLK_S_ADC "adc" |
223 | #define NPCM7XX_CLK_S_GFX "gfx0_gfx1_mem" |
224 | #define NPCM7XX_CLK_S_USBIF "serial_usbif" |
225 | #define NPCM7XX_CLK_S_USB_HOST "usb_host" |
226 | #define NPCM7XX_CLK_S_USB_BRIDGE "usb_bridge" |
227 | #define NPCM7XX_CLK_S_PCI "pci" |
228 | |
229 | static u32 pll_mux_table[] = {0, 1, 2, 3}; |
230 | static const char * const pll_mux_parents[] __initconst = { |
231 | NPCM7XX_CLK_S_PLL0, |
232 | NPCM7XX_CLK_S_PLL1_DIV2, |
233 | NPCM7XX_CLK_S_REFCLK, |
234 | NPCM7XX_CLK_S_PLL2_DIV2, |
235 | }; |
236 | |
237 | static u32 cpuck_mux_table[] = {0, 1, 2, 3}; |
238 | static const char * const cpuck_mux_parents[] __initconst = { |
239 | NPCM7XX_CLK_S_PLL0, |
240 | NPCM7XX_CLK_S_PLL1_DIV2, |
241 | NPCM7XX_CLK_S_REFCLK, |
242 | NPCM7XX_CLK_S_SYSBYPCK, |
243 | }; |
244 | |
245 | static u32 pixcksel_mux_table[] = {0, 2}; |
246 | static const char * const pixcksel_mux_parents[] __initconst = { |
247 | NPCM7XX_CLK_S_PLL_GFX, |
248 | NPCM7XX_CLK_S_REFCLK, |
249 | }; |
250 | |
251 | static u32 sucksel_mux_table[] = {2, 3}; |
252 | static const char * const sucksel_mux_parents[] __initconst = { |
253 | NPCM7XX_CLK_S_REFCLK, |
254 | NPCM7XX_CLK_S_PLL2_DIV2, |
255 | }; |
256 | |
257 | static u32 mccksel_mux_table[] = {0, 2, 3}; |
258 | static const char * const mccksel_mux_parents[] __initconst = { |
259 | NPCM7XX_CLK_S_PLL1_DIV2, |
260 | NPCM7XX_CLK_S_REFCLK, |
261 | NPCM7XX_CLK_S_MCBYPCK, |
262 | }; |
263 | |
264 | static u32 clkoutsel_mux_table[] = {0, 1, 2, 3, 4}; |
265 | static const char * const clkoutsel_mux_parents[] __initconst = { |
266 | NPCM7XX_CLK_S_PLL0, |
267 | NPCM7XX_CLK_S_PLL1_DIV2, |
268 | NPCM7XX_CLK_S_REFCLK, |
269 | NPCM7XX_CLK_S_PLL_GFX, // divided by 2 |
270 | NPCM7XX_CLK_S_PLL2_DIV2, |
271 | }; |
272 | |
273 | static u32 gfxmsel_mux_table[] = {2, 3}; |
274 | static const char * const gfxmsel_mux_parents[] __initconst = { |
275 | NPCM7XX_CLK_S_REFCLK, |
276 | NPCM7XX_CLK_S_PLL2_DIV2, |
277 | }; |
278 | |
279 | static u32 dvcssel_mux_table[] = {2, 3}; |
280 | static const char * const dvcssel_mux_parents[] __initconst = { |
281 | NPCM7XX_CLK_S_REFCLK, |
282 | NPCM7XX_CLK_S_PLL2, |
283 | }; |
284 | |
285 | static const struct npcm7xx_clk_pll_data npcm7xx_plls[] __initconst = { |
286 | {NPCM7XX_PLLCON0, NPCM7XX_CLK_S_PLL0, NPCM7XX_CLK_S_REFCLK, 0, -1}, |
287 | |
288 | {NPCM7XX_PLLCON1, NPCM7XX_CLK_S_PLL1, |
289 | NPCM7XX_CLK_S_REFCLK, 0, -1}, |
290 | |
291 | {NPCM7XX_PLLCON2, NPCM7XX_CLK_S_PLL2, |
292 | NPCM7XX_CLK_S_REFCLK, 0, -1}, |
293 | |
294 | {NPCM7XX_PLLCONG, NPCM7XX_CLK_S_PLL_GFX, |
295 | NPCM7XX_CLK_S_REFCLK, 0, -1}, |
296 | }; |
297 | |
298 | static const struct npcm7xx_clk_mux_data npcm7xx_muxes[] __initconst = { |
299 | {0, GENMASK(1, 0), cpuck_mux_table, NPCM7XX_CLK_S_CPU_MUX, |
300 | cpuck_mux_parents, ARRAY_SIZE(cpuck_mux_parents), CLK_IS_CRITICAL, |
301 | NPCM7XX_CLK_CPU}, |
302 | |
303 | {4, GENMASK(1, 0), pixcksel_mux_table, NPCM7XX_CLK_S_PIX_MUX, |
304 | pixcksel_mux_parents, ARRAY_SIZE(pixcksel_mux_parents), 0, |
305 | NPCM7XX_CLK_GFX_PIXEL}, |
306 | |
307 | {6, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_SD_MUX, |
308 | pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, |
309 | |
310 | {8, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_UART_MUX, |
311 | pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, |
312 | |
313 | {10, GENMASK(1, 0), sucksel_mux_table, NPCM7XX_CLK_S_SU_MUX, |
314 | sucksel_mux_parents, ARRAY_SIZE(sucksel_mux_parents), 0, -1}, |
315 | |
316 | {12, GENMASK(1, 0), mccksel_mux_table, NPCM7XX_CLK_S_MC_MUX, |
317 | mccksel_mux_parents, ARRAY_SIZE(mccksel_mux_parents), 0, -1}, |
318 | |
319 | {14, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_TIM_MUX, |
320 | pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, |
321 | |
322 | {16, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_GFX_MUX, |
323 | pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, |
324 | |
325 | {18, GENMASK(2, 0), clkoutsel_mux_table, NPCM7XX_CLK_S_CLKOUT_MUX, |
326 | clkoutsel_mux_parents, ARRAY_SIZE(clkoutsel_mux_parents), 0, -1}, |
327 | |
328 | {21, GENMASK(1, 0), gfxmsel_mux_table, NPCM7XX_CLK_S_GFXM_MUX, |
329 | gfxmsel_mux_parents, ARRAY_SIZE(gfxmsel_mux_parents), 0, -1}, |
330 | |
331 | {23, GENMASK(1, 0), dvcssel_mux_table, NPCM7XX_CLK_S_DVC_MUX, |
332 | dvcssel_mux_parents, ARRAY_SIZE(dvcssel_mux_parents), 0, -1}, |
333 | }; |
334 | |
335 | /* configurable dividers: */ |
336 | static const struct npcm7xx_clk_div_data npcm7xx_divs[] __initconst = { |
337 | {NPCM7XX_CLKDIV1, 28, 3, NPCM7XX_CLK_S_ADC, |
338 | NPCM7XX_CLK_S_TIMER, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_ADC}, |
339 | /*30-28 ADCCKDIV*/ |
340 | {NPCM7XX_CLKDIV1, 26, 2, NPCM7XX_CLK_S_AHB, |
341 | NPCM7XX_CLK_S_AXI, 0, CLK_IS_CRITICAL, NPCM7XX_CLK_AHB}, |
342 | /*27-26 CLK4DIV*/ |
343 | {NPCM7XX_CLKDIV1, 21, 5, NPCM7XX_CLK_S_TIMER, |
344 | NPCM7XX_CLK_S_TIM_MUX, 0, 0, NPCM7XX_CLK_TIMER}, |
345 | /*25-21 TIMCKDIV*/ |
346 | {NPCM7XX_CLKDIV1, 16, 5, NPCM7XX_CLK_S_UART, |
347 | NPCM7XX_CLK_S_UART_MUX, 0, 0, NPCM7XX_CLK_UART}, |
348 | /*20-16 UARTDIV*/ |
349 | {NPCM7XX_CLKDIV1, 11, 5, NPCM7XX_CLK_S_MMC, |
350 | NPCM7XX_CLK_S_SD_MUX, 0, 0, NPCM7XX_CLK_MMC}, |
351 | /*15-11 MMCCKDIV*/ |
352 | {NPCM7XX_CLKDIV1, 6, 5, NPCM7XX_CLK_S_SPI3, |
353 | NPCM7XX_CLK_S_AHB, 0, 0, NPCM7XX_CLK_SPI3}, |
354 | /*10-6 AHB3CKDIV*/ |
355 | {NPCM7XX_CLKDIV1, 2, 4, NPCM7XX_CLK_S_PCI, |
356 | NPCM7XX_CLK_S_GFX_MUX, 0, 0, NPCM7XX_CLK_PCI}, |
357 | /*5-2 PCICKDIV*/ |
358 | {NPCM7XX_CLKDIV1, 0, 1, NPCM7XX_CLK_S_AXI, |
359 | NPCM7XX_CLK_S_CPU_MUX, CLK_DIVIDER_POWER_OF_TWO, CLK_IS_CRITICAL, |
360 | NPCM7XX_CLK_AXI},/*0 CLK2DIV*/ |
361 | |
362 | {NPCM7XX_CLKDIV2, 30, 2, NPCM7XX_CLK_S_APB4, |
363 | NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB4}, |
364 | /*31-30 APB4CKDIV*/ |
365 | {NPCM7XX_CLKDIV2, 28, 2, NPCM7XX_CLK_S_APB3, |
366 | NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB3}, |
367 | /*29-28 APB3CKDIV*/ |
368 | {NPCM7XX_CLKDIV2, 26, 2, NPCM7XX_CLK_S_APB2, |
369 | NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB2}, |
370 | /*27-26 APB2CKDIV*/ |
371 | {NPCM7XX_CLKDIV2, 24, 2, NPCM7XX_CLK_S_APB1, |
372 | NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB1}, |
373 | /*25-24 APB1CKDIV*/ |
374 | {NPCM7XX_CLKDIV2, 22, 2, NPCM7XX_CLK_S_APB5, |
375 | NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB5}, |
376 | /*23-22 APB5CKDIV*/ |
377 | {NPCM7XX_CLKDIV2, 16, 5, NPCM7XX_CLK_S_CLKOUT, |
378 | NPCM7XX_CLK_S_CLKOUT_MUX, 0, 0, NPCM7XX_CLK_CLKOUT}, |
379 | /*20-16 CLKOUTDIV*/ |
380 | {NPCM7XX_CLKDIV2, 13, 3, NPCM7XX_CLK_S_GFX, |
381 | NPCM7XX_CLK_S_GFX_MUX, 0, 0, NPCM7XX_CLK_GFX}, |
382 | /*15-13 GFXCKDIV*/ |
383 | {NPCM7XX_CLKDIV2, 8, 5, NPCM7XX_CLK_S_USB_BRIDGE, |
384 | NPCM7XX_CLK_S_SU_MUX, 0, 0, NPCM7XX_CLK_SU}, |
385 | /*12-8 SUCKDIV*/ |
386 | {NPCM7XX_CLKDIV2, 4, 4, NPCM7XX_CLK_S_USB_HOST, |
387 | NPCM7XX_CLK_S_SU_MUX, 0, 0, NPCM7XX_CLK_SU48}, |
388 | /*7-4 SU48CKDIV*/ |
389 | {NPCM7XX_CLKDIV2, 0, 4, NPCM7XX_CLK_S_SDHC, |
390 | NPCM7XX_CLK_S_SD_MUX, 0, 0, NPCM7XX_CLK_SDHC} |
391 | ,/*3-0 SD1CKDIV*/ |
392 | |
393 | {NPCM7XX_CLKDIV3, 6, 5, NPCM7XX_CLK_S_SPI0, |
394 | NPCM7XX_CLK_S_AHB, 0, 0, NPCM7XX_CLK_SPI0}, |
395 | /*10-6 SPI0CKDV*/ |
396 | {NPCM7XX_CLKDIV3, 1, 5, NPCM7XX_CLK_S_SPIX, |
397 | NPCM7XX_CLK_S_AHB, 0, 0, NPCM7XX_CLK_SPIX}, |
398 | /*5-1 SPIXCKDV*/ |
399 | |
400 | }; |
401 | |
402 | static DEFINE_SPINLOCK(npcm7xx_clk_lock); |
403 | |
404 | static void __init npcm7xx_clk_init(struct device_node *clk_np) |
405 | { |
406 | struct clk_hw_onecell_data *npcm7xx_clk_data; |
407 | void __iomem *clk_base; |
408 | struct resource res; |
409 | struct clk_hw *hw; |
410 | int ret; |
411 | int i; |
412 | |
413 | ret = of_address_to_resource(dev: clk_np, index: 0, r: &res); |
414 | if (ret) { |
415 | pr_err("%pOFn: failed to get resource, ret %d\n" , clk_np, |
416 | ret); |
417 | return; |
418 | } |
419 | |
420 | clk_base = ioremap(offset: res.start, size: resource_size(res: &res)); |
421 | if (!clk_base) |
422 | goto npcm7xx_init_error; |
423 | |
424 | npcm7xx_clk_data = kzalloc(struct_size(npcm7xx_clk_data, hws, |
425 | NPCM7XX_NUM_CLOCKS), GFP_KERNEL); |
426 | if (!npcm7xx_clk_data) |
427 | goto npcm7xx_init_np_err; |
428 | |
429 | npcm7xx_clk_data->num = NPCM7XX_NUM_CLOCKS; |
430 | |
431 | for (i = 0; i < NPCM7XX_NUM_CLOCKS; i++) |
432 | npcm7xx_clk_data->hws[i] = ERR_PTR(error: -EPROBE_DEFER); |
433 | |
434 | /* Register plls */ |
435 | for (i = 0; i < ARRAY_SIZE(npcm7xx_plls); i++) { |
436 | const struct npcm7xx_clk_pll_data *pll_data = &npcm7xx_plls[i]; |
437 | |
438 | hw = npcm7xx_clk_register_pll(pllcon: clk_base + pll_data->reg, |
439 | name: pll_data->name, parent_name: pll_data->parent_name, flags: pll_data->flags); |
440 | if (IS_ERR(ptr: hw)) { |
441 | pr_err("npcm7xx_clk: Can't register pll\n" ); |
442 | goto npcm7xx_init_fail; |
443 | } |
444 | |
445 | if (pll_data->onecell_idx >= 0) |
446 | npcm7xx_clk_data->hws[pll_data->onecell_idx] = hw; |
447 | } |
448 | |
449 | /* Register fixed dividers */ |
450 | hw = clk_hw_register_fixed_factor(NULL, NPCM7XX_CLK_S_PLL1_DIV2, |
451 | NPCM7XX_CLK_S_PLL1, flags: 0, mult: 1, div: 2); |
452 | if (IS_ERR(ptr: hw)) { |
453 | pr_err("npcm7xx_clk: Can't register fixed div\n" ); |
454 | goto npcm7xx_init_fail; |
455 | } |
456 | |
457 | hw = clk_hw_register_fixed_factor(NULL, NPCM7XX_CLK_S_PLL2_DIV2, |
458 | NPCM7XX_CLK_S_PLL2, flags: 0, mult: 1, div: 2); |
459 | if (IS_ERR(ptr: hw)) { |
460 | pr_err("npcm7xx_clk: Can't register div2\n" ); |
461 | goto npcm7xx_init_fail; |
462 | } |
463 | |
464 | /* Register muxes */ |
465 | for (i = 0; i < ARRAY_SIZE(npcm7xx_muxes); i++) { |
466 | const struct npcm7xx_clk_mux_data *mux_data = &npcm7xx_muxes[i]; |
467 | |
468 | hw = clk_hw_register_mux_table(NULL, |
469 | mux_data->name, |
470 | mux_data->parent_names, mux_data->num_parents, |
471 | mux_data->flags, clk_base + NPCM7XX_CLKSEL, |
472 | mux_data->shift, mux_data->mask, 0, |
473 | mux_data->table, &npcm7xx_clk_lock); |
474 | |
475 | if (IS_ERR(ptr: hw)) { |
476 | pr_err("npcm7xx_clk: Can't register mux\n" ); |
477 | goto npcm7xx_init_fail; |
478 | } |
479 | |
480 | if (mux_data->onecell_idx >= 0) |
481 | npcm7xx_clk_data->hws[mux_data->onecell_idx] = hw; |
482 | } |
483 | |
484 | /* Register clock dividers specified in npcm7xx_divs */ |
485 | for (i = 0; i < ARRAY_SIZE(npcm7xx_divs); i++) { |
486 | const struct npcm7xx_clk_div_data *div_data = &npcm7xx_divs[i]; |
487 | |
488 | hw = clk_hw_register_divider(NULL, div_data->name, |
489 | div_data->parent_name, |
490 | div_data->flags, |
491 | clk_base + div_data->reg, |
492 | div_data->shift, div_data->width, |
493 | div_data->clk_divider_flags, &npcm7xx_clk_lock); |
494 | if (IS_ERR(ptr: hw)) { |
495 | pr_err("npcm7xx_clk: Can't register div table\n" ); |
496 | goto npcm7xx_init_fail; |
497 | } |
498 | |
499 | if (div_data->onecell_idx >= 0) |
500 | npcm7xx_clk_data->hws[div_data->onecell_idx] = hw; |
501 | } |
502 | |
503 | ret = of_clk_add_hw_provider(np: clk_np, get: of_clk_hw_onecell_get, |
504 | data: npcm7xx_clk_data); |
505 | if (ret) |
506 | pr_err("failed to add DT provider: %d\n" , ret); |
507 | |
508 | of_node_put(node: clk_np); |
509 | |
510 | return; |
511 | |
512 | npcm7xx_init_fail: |
513 | kfree(objp: npcm7xx_clk_data); |
514 | npcm7xx_init_np_err: |
515 | iounmap(addr: clk_base); |
516 | npcm7xx_init_error: |
517 | of_node_put(node: clk_np); |
518 | } |
519 | CLK_OF_DECLARE(npcm7xx_clk_init, "nuvoton,npcm750-clk" , npcm7xx_clk_init); |
520 | |