File Index
Files and directories
Sign in
FILE

clk-imx95-blk-ctl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/imx/clk-imx95-blk-ctl.c C
  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
 25enum {
 26	CLK_GATE,
 27	CLK_DIVIDER,
 28	CLK_MUX,
 29};
 30
 31struct 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
 42struct 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
 55struct 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
 62static 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
 95static 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
102static 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
155static 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
161static 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
218static 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
224static const char * const imx95_disp_engine_parents[] = {
225	"videopll1", "dsi_pll", "ldb_pll_div7"
226};
227
228static 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
251static 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
257static 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
280static 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
286static 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
299static 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
305static 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
319static 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
326static const char * const imx94_disp_engine_parents[] = {
327	"disppix", "ldb_pll_div7"
328};
329
330static 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
343static 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
350static 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
441cleanup:
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
452static 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
462static 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
478static 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
491static 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
509static 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
514static 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};
526MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
527
528static 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};
536module_platform_driver(imx95_bc_driver);
537
538MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
539MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
540MODULE_LICENSE("GPL");
~/ Read-only file preview.