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