1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PolarFire SoC MSS/core complex clock control |
4 | * |
5 | * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved. |
6 | */ |
7 | #include <linux/auxiliary_bus.h> |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | #include <dt-bindings/clock/microchip,mpfs-clock.h> |
14 | #include <soc/microchip/mpfs.h> |
15 | |
16 | /* address offset of control registers */ |
17 | #define REG_MSSPLL_REF_CR 0x08u |
18 | #define REG_MSSPLL_POSTDIV01_CR 0x10u |
19 | #define REG_MSSPLL_POSTDIV23_CR 0x14u |
20 | #define REG_MSSPLL_SSCG_2_CR 0x2Cu |
21 | #define REG_CLOCK_CONFIG_CR 0x08u |
22 | #define REG_RTC_CLOCK_CR 0x0Cu |
23 | #define REG_SUBBLK_CLOCK_CR 0x84u |
24 | #define REG_SUBBLK_RESET_CR 0x88u |
25 | |
26 | #define MSSPLL_FBDIV_SHIFT 0x00u |
27 | #define MSSPLL_FBDIV_WIDTH 0x0Cu |
28 | #define MSSPLL_REFDIV_SHIFT 0x08u |
29 | #define MSSPLL_REFDIV_WIDTH 0x06u |
30 | #define MSSPLL_POSTDIV02_SHIFT 0x08u |
31 | #define MSSPLL_POSTDIV13_SHIFT 0x18u |
32 | #define MSSPLL_POSTDIV_WIDTH 0x07u |
33 | #define MSSPLL_FIXED_DIV 4u |
34 | |
35 | /* |
36 | * This clock ID is defined here, rather than the binding headers, as it is an |
37 | * internal clock only, and therefore has no consumers in other peripheral |
38 | * blocks. |
39 | */ |
40 | #define CLK_MSSPLL_INTERNAL 38u |
41 | |
42 | struct mpfs_clock_data { |
43 | struct device *dev; |
44 | void __iomem *base; |
45 | void __iomem *msspll_base; |
46 | struct clk_hw_onecell_data hw_data; |
47 | }; |
48 | |
49 | struct mpfs_msspll_hw_clock { |
50 | void __iomem *base; |
51 | struct clk_hw hw; |
52 | struct clk_init_data init; |
53 | unsigned int id; |
54 | u32 reg_offset; |
55 | u32 shift; |
56 | u32 width; |
57 | u32 flags; |
58 | }; |
59 | |
60 | #define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) |
61 | |
62 | struct mpfs_msspll_out_hw_clock { |
63 | void __iomem *base; |
64 | struct clk_divider output; |
65 | struct clk_init_data init; |
66 | unsigned int id; |
67 | u32 reg_offset; |
68 | }; |
69 | |
70 | #define to_mpfs_msspll_out_clk(_hw) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw) |
71 | |
72 | struct mpfs_cfg_hw_clock { |
73 | struct clk_divider cfg; |
74 | struct clk_init_data init; |
75 | unsigned int id; |
76 | u32 reg_offset; |
77 | }; |
78 | |
79 | struct mpfs_periph_hw_clock { |
80 | struct clk_gate periph; |
81 | unsigned int id; |
82 | }; |
83 | |
84 | /* |
85 | * mpfs_clk_lock prevents anything else from writing to the |
86 | * mpfs clk block while a software locked register is being written. |
87 | */ |
88 | static DEFINE_SPINLOCK(mpfs_clk_lock); |
89 | |
90 | static const struct clk_parent_data mpfs_ext_ref[] = { |
91 | { .index = 0 }, |
92 | }; |
93 | |
94 | static const struct clk_div_table mpfs_div_cpu_axi_table[] = { |
95 | { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, |
96 | { 0, 0 } |
97 | }; |
98 | |
99 | static const struct clk_div_table mpfs_div_ahb_table[] = { |
100 | { 1, 2 }, { 2, 4}, { 3, 8 }, |
101 | { 0, 0 } |
102 | }; |
103 | |
104 | /* |
105 | * The only two supported reference clock frequencies for the PolarFire SoC are |
106 | * 100 and 125 MHz, as the rtc reference is required to be 1 MHz. |
107 | * It therefore only needs to have divider table entries corresponding to |
108 | * divide by 100 and 125. |
109 | */ |
110 | static const struct clk_div_table mpfs_div_rtcref_table[] = { |
111 | { 100, 100 }, { 125, 125 }, |
112 | { 0, 0 } |
113 | }; |
114 | |
115 | /* |
116 | * MSS PLL internal clock |
117 | */ |
118 | |
119 | static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate) |
120 | { |
121 | struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); |
122 | void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; |
123 | void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; |
124 | u32 mult, ref_div; |
125 | |
126 | mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; |
127 | mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); |
128 | ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; |
129 | ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); |
130 | |
131 | return prate * mult / (ref_div * MSSPLL_FIXED_DIV); |
132 | } |
133 | |
134 | static const struct clk_ops mpfs_clk_msspll_ops = { |
135 | .recalc_rate = mpfs_clk_msspll_recalc_rate, |
136 | }; |
137 | |
138 | #define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \ |
139 | .id = _id, \ |
140 | .flags = _flags, \ |
141 | .shift = _shift, \ |
142 | .width = _width, \ |
143 | .reg_offset = _offset, \ |
144 | .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \ |
145 | } |
146 | |
147 | static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { |
148 | CLK_PLL(CLK_MSSPLL_INTERNAL, "clk_msspll_internal" , mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, |
149 | MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR), |
150 | }; |
151 | |
152 | static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws, |
153 | unsigned int num_clks, struct mpfs_clock_data *data) |
154 | { |
155 | unsigned int i; |
156 | int ret; |
157 | |
158 | for (i = 0; i < num_clks; i++) { |
159 | struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i]; |
160 | |
161 | msspll_hw->base = data->msspll_base; |
162 | ret = devm_clk_hw_register(dev, hw: &msspll_hw->hw); |
163 | if (ret) |
164 | return dev_err_probe(dev, err: ret, fmt: "failed to register msspll id: %d\n" , |
165 | CLK_MSSPLL_INTERNAL); |
166 | |
167 | data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw; |
168 | } |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | /* |
174 | * MSS PLL output clocks |
175 | */ |
176 | |
177 | #define CLK_PLL_OUT(_id, _name, _parent, _flags, _shift, _width, _offset) { \ |
178 | .id = _id, \ |
179 | .output.shift = _shift, \ |
180 | .output.width = _width, \ |
181 | .output.table = NULL, \ |
182 | .reg_offset = _offset, \ |
183 | .output.flags = _flags, \ |
184 | .output.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \ |
185 | .output.lock = &mpfs_clk_lock, \ |
186 | } |
187 | |
188 | static struct mpfs_msspll_out_hw_clock mpfs_msspll_out_clks[] = { |
189 | CLK_PLL_OUT(CLK_MSSPLL0, "clk_msspll" , "clk_msspll_internal" , CLK_DIVIDER_ONE_BASED, |
190 | MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR), |
191 | CLK_PLL_OUT(CLK_MSSPLL1, "clk_msspll1" , "clk_msspll_internal" , CLK_DIVIDER_ONE_BASED, |
192 | MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR), |
193 | CLK_PLL_OUT(CLK_MSSPLL2, "clk_msspll2" , "clk_msspll_internal" , CLK_DIVIDER_ONE_BASED, |
194 | MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR), |
195 | CLK_PLL_OUT(CLK_MSSPLL3, "clk_msspll3" , "clk_msspll_internal" , CLK_DIVIDER_ONE_BASED, |
196 | MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR), |
197 | }; |
198 | |
199 | static int mpfs_clk_register_msspll_outs(struct device *dev, |
200 | struct mpfs_msspll_out_hw_clock *msspll_out_hws, |
201 | unsigned int num_clks, struct mpfs_clock_data *data) |
202 | { |
203 | unsigned int i; |
204 | int ret; |
205 | |
206 | for (i = 0; i < num_clks; i++) { |
207 | struct mpfs_msspll_out_hw_clock *msspll_out_hw = &msspll_out_hws[i]; |
208 | |
209 | msspll_out_hw->output.reg = data->msspll_base + msspll_out_hw->reg_offset; |
210 | ret = devm_clk_hw_register(dev, hw: &msspll_out_hw->output.hw); |
211 | if (ret) |
212 | return dev_err_probe(dev, err: ret, fmt: "failed to register msspll out id: %d\n" , |
213 | msspll_out_hw->id); |
214 | |
215 | data->hw_data.hws[msspll_out_hw->id] = &msspll_out_hw->output.hw; |
216 | } |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | /* |
222 | * "CFG" clocks |
223 | */ |
224 | |
225 | #define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \ |
226 | .id = _id, \ |
227 | .cfg.shift = _shift, \ |
228 | .cfg.width = _width, \ |
229 | .cfg.table = _table, \ |
230 | .reg_offset = _offset, \ |
231 | .cfg.flags = _flags, \ |
232 | .cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \ |
233 | .cfg.lock = &mpfs_clk_lock, \ |
234 | } |
235 | |
236 | #define CLK_CPU_OFFSET 0u |
237 | #define CLK_AXI_OFFSET 1u |
238 | #define CLK_AHB_OFFSET 2u |
239 | #define CLK_RTCREF_OFFSET 3u |
240 | |
241 | static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = { |
242 | CLK_CFG(CLK_CPU, "clk_cpu" , "clk_msspll" , 0, 2, mpfs_div_cpu_axi_table, 0, |
243 | REG_CLOCK_CONFIG_CR), |
244 | CLK_CFG(CLK_AXI, "clk_axi" , "clk_msspll" , 2, 2, mpfs_div_cpu_axi_table, 0, |
245 | REG_CLOCK_CONFIG_CR), |
246 | CLK_CFG(CLK_AHB, "clk_ahb" , "clk_msspll" , 4, 2, mpfs_div_ahb_table, 0, |
247 | REG_CLOCK_CONFIG_CR), |
248 | { |
249 | .id = CLK_RTCREF, |
250 | .cfg.shift = 0, |
251 | .cfg.width = 12, |
252 | .cfg.table = mpfs_div_rtcref_table, |
253 | .reg_offset = REG_RTC_CLOCK_CR, |
254 | .cfg.flags = CLK_DIVIDER_ONE_BASED, |
255 | .cfg.hw.init = |
256 | CLK_HW_INIT_PARENTS_DATA("clk_rtcref" , mpfs_ext_ref, &clk_divider_ops, 0), |
257 | } |
258 | }; |
259 | |
260 | static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws, |
261 | unsigned int num_clks, struct mpfs_clock_data *data) |
262 | { |
263 | unsigned int i, id; |
264 | int ret; |
265 | |
266 | for (i = 0; i < num_clks; i++) { |
267 | struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i]; |
268 | |
269 | cfg_hw->cfg.reg = data->base + cfg_hw->reg_offset; |
270 | ret = devm_clk_hw_register(dev, hw: &cfg_hw->cfg.hw); |
271 | if (ret) |
272 | return dev_err_probe(dev, err: ret, fmt: "failed to register clock id: %d\n" , |
273 | cfg_hw->id); |
274 | |
275 | id = cfg_hw->id; |
276 | data->hw_data.hws[id] = &cfg_hw->cfg.hw; |
277 | } |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | /* |
283 | * peripheral clocks - devices connected to axi or ahb buses. |
284 | */ |
285 | |
286 | #define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \ |
287 | .id = _id, \ |
288 | .periph.bit_idx = _shift, \ |
289 | .periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops, \ |
290 | _flags), \ |
291 | .periph.lock = &mpfs_clk_lock, \ |
292 | } |
293 | |
294 | #define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw) |
295 | |
296 | /* |
297 | * Critical clocks: |
298 | * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt |
299 | * trap handler |
300 | * - CLK_MMUART0: reserved by the hss |
301 | * - CLK_DDRC: provides clock to the ddr subsystem |
302 | * - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop |
303 | * if the AHB interface clock is disabled |
304 | * - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect) |
305 | * clock domain crossers which provide the interface to the FPGA fabric. Disabling them |
306 | * causes the FPGA fabric to go into reset. |
307 | * - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire. |
308 | */ |
309 | |
310 | static struct mpfs_periph_hw_clock mpfs_periph_clks[] = { |
311 | CLK_PERIPH(CLK_ENVM, "clk_periph_envm" , PARENT_CLK(AHB), 0, CLK_IS_CRITICAL), |
312 | CLK_PERIPH(CLK_MAC0, "clk_periph_mac0" , PARENT_CLK(AHB), 1, 0), |
313 | CLK_PERIPH(CLK_MAC1, "clk_periph_mac1" , PARENT_CLK(AHB), 2, 0), |
314 | CLK_PERIPH(CLK_MMC, "clk_periph_mmc" , PARENT_CLK(AHB), 3, 0), |
315 | CLK_PERIPH(CLK_TIMER, "clk_periph_timer" , PARENT_CLK(RTCREF), 4, 0), |
316 | CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0" , PARENT_CLK(AHB), 5, CLK_IS_CRITICAL), |
317 | CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1" , PARENT_CLK(AHB), 6, 0), |
318 | CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2" , PARENT_CLK(AHB), 7, 0), |
319 | CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3" , PARENT_CLK(AHB), 8, 0), |
320 | CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4" , PARENT_CLK(AHB), 9, 0), |
321 | CLK_PERIPH(CLK_SPI0, "clk_periph_spi0" , PARENT_CLK(AHB), 10, 0), |
322 | CLK_PERIPH(CLK_SPI1, "clk_periph_spi1" , PARENT_CLK(AHB), 11, 0), |
323 | CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0" , PARENT_CLK(AHB), 12, 0), |
324 | CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1" , PARENT_CLK(AHB), 13, 0), |
325 | CLK_PERIPH(CLK_CAN0, "clk_periph_can0" , PARENT_CLK(AHB), 14, 0), |
326 | CLK_PERIPH(CLK_CAN1, "clk_periph_can1" , PARENT_CLK(AHB), 15, 0), |
327 | CLK_PERIPH(CLK_USB, "clk_periph_usb" , PARENT_CLK(AHB), 16, 0), |
328 | CLK_PERIPH(CLK_RTC, "clk_periph_rtc" , PARENT_CLK(AHB), 18, CLK_IS_CRITICAL), |
329 | CLK_PERIPH(CLK_QSPI, "clk_periph_qspi" , PARENT_CLK(AHB), 19, 0), |
330 | CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0" , PARENT_CLK(AHB), 20, 0), |
331 | CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1" , PARENT_CLK(AHB), 21, 0), |
332 | CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2" , PARENT_CLK(AHB), 22, 0), |
333 | CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc" , PARENT_CLK(AHB), 23, CLK_IS_CRITICAL), |
334 | CLK_PERIPH(CLK_FIC0, "clk_periph_fic0" , PARENT_CLK(AXI), 24, CLK_IS_CRITICAL), |
335 | CLK_PERIPH(CLK_FIC1, "clk_periph_fic1" , PARENT_CLK(AXI), 25, CLK_IS_CRITICAL), |
336 | CLK_PERIPH(CLK_FIC2, "clk_periph_fic2" , PARENT_CLK(AXI), 26, CLK_IS_CRITICAL), |
337 | CLK_PERIPH(CLK_FIC3, "clk_periph_fic3" , PARENT_CLK(AXI), 27, CLK_IS_CRITICAL), |
338 | CLK_PERIPH(CLK_ATHENA, "clk_periph_athena" , PARENT_CLK(AXI), 28, CLK_IS_CRITICAL), |
339 | CLK_PERIPH(CLK_CFM, "clk_periph_cfm" , PARENT_CLK(AHB), 29, 0), |
340 | }; |
341 | |
342 | static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws, |
343 | int num_clks, struct mpfs_clock_data *data) |
344 | { |
345 | unsigned int i, id; |
346 | int ret; |
347 | |
348 | for (i = 0; i < num_clks; i++) { |
349 | struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i]; |
350 | |
351 | periph_hw->periph.reg = data->base + REG_SUBBLK_CLOCK_CR; |
352 | ret = devm_clk_hw_register(dev, hw: &periph_hw->periph.hw); |
353 | if (ret) |
354 | return dev_err_probe(dev, err: ret, fmt: "failed to register clock id: %d\n" , |
355 | periph_hw->id); |
356 | |
357 | id = periph_hws[i].id; |
358 | data->hw_data.hws[id] = &periph_hw->periph.hw; |
359 | } |
360 | |
361 | return 0; |
362 | } |
363 | |
364 | /* |
365 | * Peripheral clock resets |
366 | */ |
367 | |
368 | #if IS_ENABLED(CONFIG_RESET_CONTROLLER) |
369 | |
370 | u32 mpfs_reset_read(struct device *dev) |
371 | { |
372 | struct mpfs_clock_data *clock_data = dev_get_drvdata(dev: dev->parent); |
373 | |
374 | return readl_relaxed(clock_data->base + REG_SUBBLK_RESET_CR); |
375 | } |
376 | EXPORT_SYMBOL_NS_GPL(mpfs_reset_read, MCHP_CLK_MPFS); |
377 | |
378 | void mpfs_reset_write(struct device *dev, u32 val) |
379 | { |
380 | struct mpfs_clock_data *clock_data = dev_get_drvdata(dev: dev->parent); |
381 | |
382 | writel_relaxed(val, clock_data->base + REG_SUBBLK_RESET_CR); |
383 | } |
384 | EXPORT_SYMBOL_NS_GPL(mpfs_reset_write, MCHP_CLK_MPFS); |
385 | |
386 | static void mpfs_reset_unregister_adev(void *_adev) |
387 | { |
388 | struct auxiliary_device *adev = _adev; |
389 | |
390 | auxiliary_device_delete(auxdev: adev); |
391 | auxiliary_device_uninit(auxdev: adev); |
392 | } |
393 | |
394 | static void mpfs_reset_adev_release(struct device *dev) |
395 | { |
396 | struct auxiliary_device *adev = to_auxiliary_dev(dev); |
397 | |
398 | kfree(objp: adev); |
399 | } |
400 | |
401 | static struct auxiliary_device *mpfs_reset_adev_alloc(struct mpfs_clock_data *clk_data) |
402 | { |
403 | struct auxiliary_device *adev; |
404 | int ret; |
405 | |
406 | adev = kzalloc(size: sizeof(*adev), GFP_KERNEL); |
407 | if (!adev) |
408 | return ERR_PTR(error: -ENOMEM); |
409 | |
410 | adev->name = "reset-mpfs" ; |
411 | adev->dev.parent = clk_data->dev; |
412 | adev->dev.release = mpfs_reset_adev_release; |
413 | adev->id = 666u; |
414 | |
415 | ret = auxiliary_device_init(auxdev: adev); |
416 | if (ret) { |
417 | kfree(objp: adev); |
418 | return ERR_PTR(error: ret); |
419 | } |
420 | |
421 | return adev; |
422 | } |
423 | |
424 | static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data) |
425 | { |
426 | struct auxiliary_device *adev; |
427 | int ret; |
428 | |
429 | adev = mpfs_reset_adev_alloc(clk_data); |
430 | if (IS_ERR(ptr: adev)) |
431 | return PTR_ERR(ptr: adev); |
432 | |
433 | ret = auxiliary_device_add(adev); |
434 | if (ret) { |
435 | auxiliary_device_uninit(auxdev: adev); |
436 | return ret; |
437 | } |
438 | |
439 | return devm_add_action_or_reset(clk_data->dev, mpfs_reset_unregister_adev, adev); |
440 | } |
441 | |
442 | #else /* !CONFIG_RESET_CONTROLLER */ |
443 | |
444 | static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data) |
445 | { |
446 | return 0; |
447 | } |
448 | |
449 | #endif /* !CONFIG_RESET_CONTROLLER */ |
450 | |
451 | static int mpfs_clk_probe(struct platform_device *pdev) |
452 | { |
453 | struct device *dev = &pdev->dev; |
454 | struct mpfs_clock_data *clk_data; |
455 | unsigned int num_clks; |
456 | int ret; |
457 | |
458 | /* CLK_RESERVED is not part of clock arrays, so add 1 */ |
459 | num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_msspll_out_clks) |
460 | + ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1; |
461 | |
462 | clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL); |
463 | if (!clk_data) |
464 | return -ENOMEM; |
465 | |
466 | clk_data->base = devm_platform_ioremap_resource(pdev, index: 0); |
467 | if (IS_ERR(ptr: clk_data->base)) |
468 | return PTR_ERR(ptr: clk_data->base); |
469 | |
470 | clk_data->msspll_base = devm_platform_ioremap_resource(pdev, index: 1); |
471 | if (IS_ERR(ptr: clk_data->msspll_base)) |
472 | return PTR_ERR(ptr: clk_data->msspll_base); |
473 | |
474 | clk_data->hw_data.num = num_clks; |
475 | clk_data->dev = dev; |
476 | dev_set_drvdata(dev, data: clk_data); |
477 | |
478 | ret = mpfs_clk_register_mssplls(dev, msspll_hws: mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks), |
479 | data: clk_data); |
480 | if (ret) |
481 | return ret; |
482 | |
483 | ret = mpfs_clk_register_msspll_outs(dev, msspll_out_hws: mpfs_msspll_out_clks, |
484 | ARRAY_SIZE(mpfs_msspll_out_clks), |
485 | data: clk_data); |
486 | if (ret) |
487 | return ret; |
488 | |
489 | ret = mpfs_clk_register_cfgs(dev, cfg_hws: mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), data: clk_data); |
490 | if (ret) |
491 | return ret; |
492 | |
493 | ret = mpfs_clk_register_periphs(dev, periph_hws: mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks), |
494 | data: clk_data); |
495 | if (ret) |
496 | return ret; |
497 | |
498 | ret = devm_of_clk_add_hw_provider(dev, get: of_clk_hw_onecell_get, data: &clk_data->hw_data); |
499 | if (ret) |
500 | return ret; |
501 | |
502 | return mpfs_reset_controller_register(clk_data); |
503 | } |
504 | |
505 | static const struct of_device_id mpfs_clk_of_match_table[] = { |
506 | { .compatible = "microchip,mpfs-clkcfg" , }, |
507 | {} |
508 | }; |
509 | MODULE_DEVICE_TABLE(of, mpfs_clk_of_match_table); |
510 | |
511 | static struct platform_driver mpfs_clk_driver = { |
512 | .probe = mpfs_clk_probe, |
513 | .driver = { |
514 | .name = "microchip-mpfs-clkcfg" , |
515 | .of_match_table = mpfs_clk_of_match_table, |
516 | }, |
517 | }; |
518 | |
519 | static int __init clk_mpfs_init(void) |
520 | { |
521 | return platform_driver_register(&mpfs_clk_driver); |
522 | } |
523 | core_initcall(clk_mpfs_init); |
524 | |
525 | static void __exit clk_mpfs_exit(void) |
526 | { |
527 | platform_driver_unregister(&mpfs_clk_driver); |
528 | } |
529 | module_exit(clk_mpfs_exit); |
530 | |
531 | MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver" ); |
532 | MODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>" ); |
533 | MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>" ); |
534 | MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>" ); |
535 | |