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