xref: /linux/drivers/clk/imx/clk-imx95-blk-ctl.c (revision 502d45774af09f1c681c754c4b7cdfb5d7f72fd9)
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