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