1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2024-2025 NXP
4 */
5
6 #include <dt-bindings/clock/nxp,imx94-clock.h>
7 #include <dt-bindings/clock/nxp,imx95-clock.h>
8 #include <linux/clk.h>
9 #include <linux/clk-provider.h>
10 #include <linux/pm_runtime.h>
11 #include <linux/debugfs.h>
12 #include <linux/device.h>
13 #include <linux/err.h>
14 #include <linux/io.h>
15 #include <linux/module.h>
16 #include <linux/of_address.h>
17 #include <linux/of_device.h>
18 #include <linux/of_platform.h>
19 #include <linux/platform_device.h>
20 #include <linux/regmap.h>
21 #include <linux/slab.h>
22 #include <linux/spinlock.h>
23 #include <linux/types.h>
24
25 enum {
26 CLK_GATE,
27 CLK_DIVIDER,
28 CLK_MUX,
29 };
30
31 struct imx95_blk_ctl {
32 struct device *dev;
33 spinlock_t lock;
34 struct clk *clk_apb;
35
36 void __iomem *base;
37 /* clock gate register */
38 u32 clk_reg_restore;
39 const struct imx95_blk_ctl_dev_data *pdata;
40 };
41
42 struct imx95_blk_ctl_clk_dev_data {
43 const char *name;
44 const char * const *parent_names;
45 u32 num_parents;
46 u32 reg;
47 u32 reg_init_msk;
48 u32 reg_init_val;
49 u32 bit_idx;
50 u32 bit_width;
51 u32 clk_type;
52 u32 flags;
53 u32 flags2;
54 u32 type;
55 };
56
57 struct imx95_blk_ctl_dev_data {
58 const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;
59 u32 num_clks;
60 bool rpm_enabled;
61 u32 clk_reg_offset;
62 };
63
64 static const struct imx95_blk_ctl_clk_dev_data vpublk_clk_dev_data[] = {
65 [IMX95_CLK_VPUBLK_WAVE] = {
66 .name = "vpublk_wave_vpu",
67 .parent_names = (const char *[]){ "vpu", },
68 .num_parents = 1,
69 .reg = 8,
70 .bit_idx = 0,
71 .type = CLK_GATE,
72 .flags = CLK_SET_RATE_PARENT,
73 .flags2 = CLK_GATE_SET_TO_DISABLE,
74 },
75 [IMX95_CLK_VPUBLK_JPEG_ENC] = {
76 .name = "vpublk_jpeg_enc",
77 .parent_names = (const char *[]){ "vpujpeg", },
78 .num_parents = 1,
79 .reg = 8,
80 .bit_idx = 1,
81 .type = CLK_GATE,
82 .flags = CLK_SET_RATE_PARENT,
83 .flags2 = CLK_GATE_SET_TO_DISABLE,
84 },
85 [IMX95_CLK_VPUBLK_JPEG_DEC] = {
86 .name = "vpublk_jpeg_dec",
87 .parent_names = (const char *[]){ "vpujpeg", },
88 .num_parents = 1,
89 .reg = 8,
90 .bit_idx = 2,
91 .type = CLK_GATE,
92 .flags = CLK_SET_RATE_PARENT,
93 .flags2 = CLK_GATE_SET_TO_DISABLE,
94 }
95 };
96
97 static const struct imx95_blk_ctl_dev_data vpublk_dev_data = {
98 .num_clks = ARRAY_SIZE(vpublk_clk_dev_data),
99 .clk_dev_data = vpublk_clk_dev_data,
100 .rpm_enabled = true,
101 .clk_reg_offset = 8,
102 };
103
104 static const struct imx95_blk_ctl_clk_dev_data camblk_clk_dev_data[] = {
105 [IMX95_CLK_CAMBLK_CSI2_FOR0] = {
106 .name = "camblk_csi2_for0",
107 .parent_names = (const char *[]){ "camisi", },
108 .num_parents = 1,
109 .reg = 0,
110 .bit_idx = 0,
111 .type = CLK_GATE,
112 .flags = CLK_SET_RATE_PARENT,
113 .flags2 = CLK_GATE_SET_TO_DISABLE,
114 },
115 [IMX95_CLK_CAMBLK_CSI2_FOR1] = {
116 .name = "camblk_csi2_for1",
117 .parent_names = (const char *[]){ "camisi", },
118 .num_parents = 1,
119 .reg = 0,
120 .bit_idx = 1,
121 .type = CLK_GATE,
122 .flags = CLK_SET_RATE_PARENT,
123 .flags2 = CLK_GATE_SET_TO_DISABLE,
124 },
125 [IMX95_CLK_CAMBLK_ISP_AXI] = {
126 .name = "camblk_isp_axi",
127 .parent_names = (const char *[]){ "camaxi", },
128 .num_parents = 1,
129 .reg = 0,
130 .bit_idx = 4,
131 .type = CLK_GATE,
132 .flags = CLK_SET_RATE_PARENT,
133 .flags2 = CLK_GATE_SET_TO_DISABLE,
134 },
135 [IMX95_CLK_CAMBLK_ISP_PIXEL] = {
136 .name = "camblk_isp_pixel",
137 .parent_names = (const char *[]){ "camisi", },
138 .num_parents = 1,
139 .reg = 0,
140 .bit_idx = 5,
141 .type = CLK_GATE,
142 .flags = CLK_SET_RATE_PARENT,
143 .flags2 = CLK_GATE_SET_TO_DISABLE,
144 },
145 [IMX95_CLK_CAMBLK_ISP] = {
146 .name = "camblk_isp",
147 .parent_names = (const char *[]){ "camisi", },
148 .num_parents = 1,
149 .reg = 0,
150 .bit_idx = 6,
151 .type = CLK_GATE,
152 .flags = CLK_SET_RATE_PARENT,
153 .flags2 = CLK_GATE_SET_TO_DISABLE,
154 }
155 };
156
157 static const struct imx95_blk_ctl_dev_data camblk_dev_data = {
158 .num_clks = ARRAY_SIZE(camblk_clk_dev_data),
159 .clk_dev_data = camblk_clk_dev_data,
160 .clk_reg_offset = 0,
161 };
162
163 static const struct imx95_blk_ctl_clk_dev_data imx95_lvds_clk_dev_data[] = {
164 [IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = {
165 .name = "ldb_phy_div",
166 .parent_names = (const char *[]){ "ldbpll", },
167 .num_parents = 1,
168 .reg = 0,
169 .bit_idx = 0,
170 .bit_width = 1,
171 .type = CLK_DIVIDER,
172 .flags2 = CLK_DIVIDER_POWER_OF_TWO,
173 },
174 [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {
175 .name = "lvds_ch0_gate",
176 .parent_names = (const char *[]){ "ldb_phy_div", },
177 .num_parents = 1,
178 .reg = 0,
179 .bit_idx = 1,
180 .bit_width = 1,
181 .type = CLK_GATE,
182 .flags = CLK_SET_RATE_PARENT,
183 .flags2 = CLK_GATE_SET_TO_DISABLE,
184 },
185 [IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = {
186 .name = "lvds_ch1_gate",
187 .parent_names = (const char *[]){ "ldb_phy_div", },
188 .num_parents = 1,
189 .reg = 0,
190 .bit_idx = 2,
191 .bit_width = 1,
192 .type = CLK_GATE,
193 .flags = CLK_SET_RATE_PARENT,
194 .flags2 = CLK_GATE_SET_TO_DISABLE,
195 },
196 [IMX95_CLK_DISPMIX_PIX_DI0_GATE] = {
197 .name = "lvds_di0_gate",
198 .parent_names = (const char *[]){ "ldb_pll_div7", },
199 .num_parents = 1,
200 .reg = 0,
201 .bit_idx = 3,
202 .bit_width = 1,
203 .type = CLK_GATE,
204 .flags = CLK_SET_RATE_PARENT,
205 .flags2 = CLK_GATE_SET_TO_DISABLE,
206 },
207 [IMX95_CLK_DISPMIX_PIX_DI1_GATE] = {
208 .name = "lvds_di1_gate",
209 .parent_names = (const char *[]){ "ldb_pll_div7", },
210 .num_parents = 1,
211 .reg = 0,
212 .bit_idx = 4,
213 .bit_width = 1,
214 .type = CLK_GATE,
215 .flags = CLK_SET_RATE_PARENT,
216 .flags2 = CLK_GATE_SET_TO_DISABLE,
217 },
218 };
219
220 static const struct imx95_blk_ctl_dev_data imx95_lvds_csr_dev_data = {
221 .num_clks = ARRAY_SIZE(imx95_lvds_clk_dev_data),
222 .clk_dev_data = imx95_lvds_clk_dev_data,
223 .clk_reg_offset = 0,
224 };
225
226 static const char * const imx95_disp_engine_parents[] = {
227 "videopll1", "dsi_pll", "ldb_pll_div7"
228 };
229
230 static const struct imx95_blk_ctl_clk_dev_data imx95_dispmix_csr_clk_dev_data[] = {
231 [IMX95_CLK_DISPMIX_ENG0_SEL] = {
232 .name = "disp_engine0_sel",
233 .parent_names = imx95_disp_engine_parents,
234 .num_parents = ARRAY_SIZE(imx95_disp_engine_parents),
235 .reg = 0,
236 .bit_idx = 0,
237 .bit_width = 2,
238 .type = CLK_MUX,
239 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
240 },
241 [IMX95_CLK_DISPMIX_ENG1_SEL] = {
242 .name = "disp_engine1_sel",
243 .parent_names = imx95_disp_engine_parents,
244 .num_parents = ARRAY_SIZE(imx95_disp_engine_parents),
245 .reg = 0,
246 .bit_idx = 2,
247 .bit_width = 2,
248 .type = CLK_MUX,
249 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
250 }
251 };
252
253 static const struct imx95_blk_ctl_dev_data imx95_dispmix_csr_dev_data = {
254 .num_clks = ARRAY_SIZE(imx95_dispmix_csr_clk_dev_data),
255 .clk_dev_data = imx95_dispmix_csr_clk_dev_data,
256 .clk_reg_offset = 0,
257 };
258
259 static const struct imx95_blk_ctl_clk_dev_data netxmix_clk_dev_data[] = {
260 [IMX95_CLK_NETCMIX_ENETC0_RMII] = {
261 .name = "enetc0_rmii_sel",
262 .parent_names = (const char *[]){"ext_enetref", "enetref"},
263 .num_parents = 2,
264 .reg = 4,
265 .bit_idx = 5,
266 .bit_width = 1,
267 .type = CLK_MUX,
268 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
269 },
270 [IMX95_CLK_NETCMIX_ENETC1_RMII] = {
271 .name = "enetc1_rmii_sel",
272 .parent_names = (const char *[]){"ext_enetref", "enetref"},
273 .num_parents = 2,
274 .reg = 4,
275 .bit_idx = 10,
276 .bit_width = 1,
277 .type = CLK_MUX,
278 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
279 },
280 };
281
282 static const struct imx95_blk_ctl_dev_data netcmix_dev_data = {
283 .num_clks = ARRAY_SIZE(netxmix_clk_dev_data),
284 .clk_dev_data = netxmix_clk_dev_data,
285 .clk_reg_offset = 0,
286 };
287
288 static const struct imx95_blk_ctl_clk_dev_data hsio_blk_ctl_clk_dev_data[] = {
289 [0] = {
290 .name = "hsio_blk_ctl_clk",
291 .parent_names = (const char *[]){ "func_out_en", },
292 .num_parents = 1,
293 .reg = 0,
294 .reg_init_msk = GENMASK(10, 7),
295 .reg_init_val = GENMASK(10, 7),
296 .bit_idx = 6,
297 .bit_width = 1,
298 .type = CLK_GATE,
299 .flags = CLK_SET_RATE_PARENT,
300 },
301 [1] = {
302 .name = "func_out_en",
303 .parent_names = (const char *[]){ "hsio_pll", },
304 .num_parents = 1,
305 .reg = 0,
306 .bit_idx = 2,
307 .bit_width = 1,
308 .type = CLK_GATE,
309 .flags = CLK_SET_RATE_PARENT,
310 }
311 };
312
313 static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = {
314 .num_clks = ARRAY_SIZE(hsio_blk_ctl_clk_dev_data),
315 .clk_dev_data = hsio_blk_ctl_clk_dev_data,
316 .clk_reg_offset = 0,
317 };
318
319 static const struct imx95_blk_ctl_clk_dev_data imx94_lvds_clk_dev_data[] = {
320 [IMX94_CLK_DISPMIX_LVDS_CLK_GATE] = {
321 .name = "lvds_clk_gate",
322 .parent_names = (const char *[]){ "ldbpll", },
323 .num_parents = 1,
324 .reg = 0,
325 .bit_idx = 1,
326 .bit_width = 1,
327 .type = CLK_GATE,
328 .flags = CLK_SET_RATE_PARENT,
329 .flags2 = CLK_GATE_SET_TO_DISABLE,
330 },
331 };
332
333 static const struct imx95_blk_ctl_dev_data imx94_lvds_csr_dev_data = {
334 .num_clks = ARRAY_SIZE(imx94_lvds_clk_dev_data),
335 .clk_dev_data = imx94_lvds_clk_dev_data,
336 .clk_reg_offset = 0,
337 .rpm_enabled = true,
338 };
339
340 static const char * const imx94_disp_engine_parents[] = {
341 "disppix", "ldb_pll_div7"
342 };
343
344 static const struct imx95_blk_ctl_clk_dev_data imx94_dispmix_csr_clk_dev_data[] = {
345 [IMX94_CLK_DISPMIX_CLK_SEL] = {
346 .name = "disp_clk_sel",
347 .parent_names = imx94_disp_engine_parents,
348 .num_parents = ARRAY_SIZE(imx94_disp_engine_parents),
349 .reg = 0,
350 .bit_idx = 1,
351 .bit_width = 1,
352 .type = CLK_MUX,
353 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
354 },
355 };
356
357 static const struct imx95_blk_ctl_dev_data imx94_dispmix_csr_dev_data = {
358 .num_clks = ARRAY_SIZE(imx94_dispmix_csr_clk_dev_data),
359 .clk_dev_data = imx94_dispmix_csr_clk_dev_data,
360 .clk_reg_offset = 0,
361 .rpm_enabled = true,
362 };
363
imx95_bc_probe(struct platform_device * pdev)364 static int imx95_bc_probe(struct platform_device *pdev)
365 {
366 struct device *dev = &pdev->dev;
367 struct imx95_blk_ctl *bc;
368 struct clk_hw_onecell_data *clk_hw_data;
369 struct clk_hw **hws;
370 void __iomem *base;
371 int i, ret;
372
373 bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
374 if (!bc)
375 return -ENOMEM;
376 bc->dev = dev;
377 dev_set_drvdata(&pdev->dev, bc);
378
379 spin_lock_init(&bc->lock);
380
381 base = devm_platform_ioremap_resource(pdev, 0);
382 if (IS_ERR(base))
383 return PTR_ERR(base);
384
385 bc->base = base;
386 bc->clk_apb = devm_clk_get(dev, NULL);
387 if (IS_ERR(bc->clk_apb))
388 return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n");
389
390 ret = clk_prepare_enable(bc->clk_apb);
391 if (ret) {
392 dev_err(dev, "failed to enable apb clock: %d\n", ret);
393 return ret;
394 }
395
396 bc->pdata = of_device_get_match_data(dev);
397 if (!bc->pdata)
398 return devm_of_platform_populate(dev);
399
400 clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc->pdata->num_clks),
401 GFP_KERNEL);
402 if (!clk_hw_data)
403 return -ENOMEM;
404
405 if (bc->pdata->rpm_enabled) {
406 devm_pm_runtime_enable(&pdev->dev);
407 pm_runtime_resume_and_get(&pdev->dev);
408 }
409
410 clk_hw_data->num = bc->pdata->num_clks;
411 hws = clk_hw_data->hws;
412
413 for (i = 0; i < bc->pdata->num_clks; i++) {
414 const struct imx95_blk_ctl_clk_dev_data *data = &bc->pdata->clk_dev_data[i];
415 void __iomem *reg = base + data->reg;
416
417 if (data->reg_init_msk)
418 writel((readl(reg) & ~data->reg_init_msk) | data->reg_init_val, reg);
419
420 if (data->type == CLK_MUX) {
421 hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names,
422 data->num_parents, data->flags, reg,
423 data->bit_idx, data->bit_width,
424 data->flags2, &bc->lock);
425 } else if (data->type == CLK_DIVIDER) {
426 hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0],
427 data->flags, reg, data->bit_idx,
428 data->bit_width, data->flags2, &bc->lock);
429 } else {
430 hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0],
431 data->flags, reg, data->bit_idx,
432 data->flags2, &bc->lock);
433 }
434 if (IS_ERR(hws[i])) {
435 ret = PTR_ERR(hws[i]);
436 dev_err(dev, "failed to register: %s:%d\n", data->name, ret);
437 goto cleanup;
438 }
439 }
440
441 ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data);
442 if (ret)
443 goto cleanup;
444
445 ret = devm_of_platform_populate(dev);
446 if (ret) {
447 of_clk_del_provider(dev->of_node);
448 goto cleanup;
449 }
450
451 if (pm_runtime_enabled(bc->dev)) {
452 pm_runtime_put_sync(&pdev->dev);
453 clk_disable_unprepare(bc->clk_apb);
454 }
455
456 return 0;
457
458 cleanup:
459 for (i = 0; i < bc->pdata->num_clks; i++) {
460 if (IS_ERR_OR_NULL(hws[i]))
461 continue;
462 clk_hw_unregister(hws[i]);
463 }
464
465 return ret;
466 }
467
468 #ifdef CONFIG_PM
imx95_bc_runtime_suspend(struct device * dev)469 static int imx95_bc_runtime_suspend(struct device *dev)
470 {
471 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
472
473 bc->clk_reg_restore = readl(bc->base + bc->pdata->clk_reg_offset);
474 clk_disable_unprepare(bc->clk_apb);
475
476 return 0;
477 }
478
imx95_bc_runtime_resume(struct device * dev)479 static int imx95_bc_runtime_resume(struct device *dev)
480 {
481 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
482 int ret;
483
484 ret = clk_prepare_enable(bc->clk_apb);
485 if (ret)
486 return ret;
487
488 writel(bc->clk_reg_restore, bc->base + bc->pdata->clk_reg_offset);
489
490 return 0;
491 }
492 #endif
493
494 #ifdef CONFIG_PM_SLEEP
imx95_bc_suspend(struct device * dev)495 static int imx95_bc_suspend(struct device *dev)
496 {
497 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
498
499 if (pm_runtime_suspended(dev))
500 return 0;
501
502 bc->clk_reg_restore = readl(bc->base + bc->pdata->clk_reg_offset);
503 clk_disable_unprepare(bc->clk_apb);
504
505 return 0;
506 }
507
imx95_bc_resume(struct device * dev)508 static int imx95_bc_resume(struct device *dev)
509 {
510 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
511 int ret;
512
513 if (pm_runtime_suspended(dev))
514 return 0;
515
516 ret = clk_prepare_enable(bc->clk_apb);
517 if (ret)
518 return ret;
519
520 writel(bc->clk_reg_restore, bc->base + bc->pdata->clk_reg_offset);
521
522 return 0;
523 }
524 #endif
525
526 static const struct dev_pm_ops imx95_bc_pm_ops = {
527 SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL)
528 SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume)
529 };
530
531 static const struct of_device_id imx95_bc_of_match[] = {
532 { .compatible = "nxp,imx94-display-csr", .data = &imx94_dispmix_csr_dev_data },
533 { .compatible = "nxp,imx94-lvds-csr", .data = &imx94_lvds_csr_dev_data },
534 { .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data },
535 { .compatible = "nxp,imx95-display-master-csr", },
536 { .compatible = "nxp,imx95-display-csr", .data = &imx95_dispmix_csr_dev_data },
537 { .compatible = "nxp,imx95-lvds-csr", .data = &imx95_lvds_csr_dev_data },
538 { .compatible = "nxp,imx95-hsio-blk-ctl", .data = &hsio_blk_ctl_dev_data },
539 { .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data },
540 { .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data},
541 { /* Sentinel */ },
542 };
543 MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
544
545 static struct platform_driver imx95_bc_driver = {
546 .probe = imx95_bc_probe,
547 .driver = {
548 .name = "imx95-blk-ctl",
549 .of_match_table = imx95_bc_of_match,
550 .pm = &imx95_bc_pm_ops,
551 },
552 };
553 module_platform_driver(imx95_bc_driver);
554
555 MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
556 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
557 MODULE_LICENSE("GPL");
558