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