Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 24enum {
 25	CLK_GATE,
 26	CLK_DIVIDER,
 27	CLK_MUX,
 28};
 29
 30struct 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
 40struct 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
 53struct 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
 60static 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
 93static 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
100static 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
153static 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
159static 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
216static 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
222static const struct imx95_blk_ctl_clk_dev_data dispmix_csr_clk_dev_data[] = {
223	[IMX95_CLK_DISPMIX_ENG0_SEL] = {
224		.name = "disp_engine0_sel",
225		.parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },
226		.num_parents = 4,
227		.reg = 0,
228		.bit_idx = 0,
229		.bit_width = 2,
230		.type = CLK_MUX,
231		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
232	},
233	[IMX95_CLK_DISPMIX_ENG1_SEL] = {
234		.name = "disp_engine1_sel",
235		.parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },
236		.num_parents = 4,
237		.reg = 0,
238		.bit_idx = 2,
239		.bit_width = 2,
240		.type = CLK_MUX,
241		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
242	}
243};
244
245static const struct imx95_blk_ctl_dev_data dispmix_csr_dev_data = {
246	.num_clks = ARRAY_SIZE(dispmix_csr_clk_dev_data),
247	.clk_dev_data = dispmix_csr_clk_dev_data,
248	.clk_reg_offset = 0,
249};
250
251static const struct imx95_blk_ctl_clk_dev_data netxmix_clk_dev_data[] = {
252	[IMX95_CLK_NETCMIX_ENETC0_RMII] = {
253		.name = "enetc0_rmii_sel",
254		.parent_names = (const char *[]){"ext_enetref", "enetref"},
255		.num_parents = 2,
256		.reg = 4,
257		.bit_idx = 5,
258		.bit_width = 1,
259		.type = CLK_MUX,
260		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
261	},
262	[IMX95_CLK_NETCMIX_ENETC1_RMII] = {
263		.name = "enetc1_rmii_sel",
264		.parent_names = (const char *[]){"ext_enetref", "enetref"},
265		.num_parents = 2,
266		.reg = 4,
267		.bit_idx = 10,
268		.bit_width = 1,
269		.type = CLK_MUX,
270		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
271	},
272};
273
274static const struct imx95_blk_ctl_dev_data netcmix_dev_data = {
275	.num_clks = ARRAY_SIZE(netxmix_clk_dev_data),
276	.clk_dev_data = netxmix_clk_dev_data,
277	.clk_reg_offset = 0,
278};
279
280static const struct imx95_blk_ctl_clk_dev_data hsio_blk_ctl_clk_dev_data[] = {
281	[0] = {
282		.name = "hsio_blk_ctl_clk",
283		.parent_names = (const char *[]){ "hsio_pll", },
284		.num_parents = 1,
285		.reg = 0,
286		.bit_idx = 6,
287		.bit_width = 1,
288		.type = CLK_GATE,
289		.flags = CLK_SET_RATE_PARENT,
290	}
291};
292
293static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = {
294	.num_clks = 1,
295	.clk_dev_data = hsio_blk_ctl_clk_dev_data,
296	.clk_reg_offset = 0,
297};
298
299static int imx95_bc_probe(struct platform_device *pdev)
300{
301	struct device *dev = &pdev->dev;
302	const struct imx95_blk_ctl_dev_data *bc_data;
303	struct imx95_blk_ctl *bc;
304	struct clk_hw_onecell_data *clk_hw_data;
305	struct clk_hw **hws;
306	void __iomem *base;
307	int i, ret;
308
309	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
310	if (!bc)
311		return -ENOMEM;
312	bc->dev = dev;
313	dev_set_drvdata(&pdev->dev, bc);
314
315	spin_lock_init(&bc->lock);
316
317	base = devm_platform_ioremap_resource(pdev, 0);
318	if (IS_ERR(base))
319		return PTR_ERR(base);
320
321	bc->base = base;
322	bc->clk_apb = devm_clk_get(dev, NULL);
323	if (IS_ERR(bc->clk_apb))
324		return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n");
325
326	ret = clk_prepare_enable(bc->clk_apb);
327	if (ret) {
328		dev_err(dev, "failed to enable apb clock: %d\n", ret);
329		return ret;
330	}
331
332	bc_data = of_device_get_match_data(dev);
333	if (!bc_data)
334		return devm_of_platform_populate(dev);
335
336	clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc_data->num_clks),
337				   GFP_KERNEL);
338	if (!clk_hw_data)
339		return -ENOMEM;
340
341	if (bc_data->rpm_enabled)
342		pm_runtime_enable(&pdev->dev);
343
344	clk_hw_data->num = bc_data->num_clks;
345	hws = clk_hw_data->hws;
346
347	for (i = 0; i < bc_data->num_clks; i++) {
348		const struct imx95_blk_ctl_clk_dev_data *data = &bc_data->clk_dev_data[i];
349		void __iomem *reg = base + data->reg;
350
351		if (data->type == CLK_MUX) {
352			hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names,
353						     data->num_parents, data->flags, reg,
354						     data->bit_idx, data->bit_width,
355						     data->flags2, &bc->lock);
356		} else if (data->type == CLK_DIVIDER) {
357			hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0],
358							 data->flags, reg, data->bit_idx,
359							 data->bit_width, data->flags2, &bc->lock);
360		} else {
361			hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0],
362						      data->flags, reg, data->bit_idx,
363						      data->flags2, &bc->lock);
364		}
365		if (IS_ERR(hws[i])) {
366			ret = PTR_ERR(hws[i]);
367			dev_err(dev, "failed to register: %s:%d\n", data->name, ret);
368			goto cleanup;
369		}
370	}
371
372	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data);
373	if (ret)
374		goto cleanup;
375
376	ret = devm_of_platform_populate(dev);
377	if (ret) {
378		of_clk_del_provider(dev->of_node);
379		goto cleanup;
380	}
381
382	if (pm_runtime_enabled(bc->dev))
383		clk_disable_unprepare(bc->clk_apb);
384
385	return 0;
386
387cleanup:
388	for (i = 0; i < bc_data->num_clks; i++) {
389		if (IS_ERR_OR_NULL(hws[i]))
390			continue;
391		clk_hw_unregister(hws[i]);
392	}
393
394	if (bc_data->rpm_enabled)
395		pm_runtime_disable(&pdev->dev);
396
397	return ret;
398}
399
400#ifdef CONFIG_PM
401static int imx95_bc_runtime_suspend(struct device *dev)
402{
403	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
404
405	clk_disable_unprepare(bc->clk_apb);
406	return 0;
407}
408
409static int imx95_bc_runtime_resume(struct device *dev)
410{
411	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
412
413	return clk_prepare_enable(bc->clk_apb);
414}
415#endif
416
417#ifdef CONFIG_PM_SLEEP
418static int imx95_bc_suspend(struct device *dev)
419{
420	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
421	const struct imx95_blk_ctl_dev_data *bc_data;
422	int ret;
423
424	bc_data = of_device_get_match_data(dev);
425	if (!bc_data)
426		return 0;
427
428	if (bc_data->rpm_enabled) {
429		ret = pm_runtime_get_sync(bc->dev);
430		if (ret < 0) {
431			pm_runtime_put_noidle(bc->dev);
432			return ret;
433		}
434	}
435
436	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
437
438	return 0;
439}
440
441static int imx95_bc_resume(struct device *dev)
442{
443	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
444	const struct imx95_blk_ctl_dev_data *bc_data;
445
446	bc_data = of_device_get_match_data(dev);
447	if (!bc_data)
448		return 0;
449
450	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
451
452	if (bc_data->rpm_enabled)
453		pm_runtime_put(bc->dev);
454
455	return 0;
456}
457#endif
458
459static const struct dev_pm_ops imx95_bc_pm_ops = {
460	SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL)
461	SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume)
462};
463
464static const struct of_device_id imx95_bc_of_match[] = {
465	{ .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data },
466	{ .compatible = "nxp,imx95-display-master-csr", },
467	{ .compatible = "nxp,imx95-lvds-csr", .data = &lvds_csr_dev_data },
468	{ .compatible = "nxp,imx95-display-csr", .data = &dispmix_csr_dev_data },
469	{ .compatible = "nxp,imx95-hsio-blk-ctl", .data = &hsio_blk_ctl_dev_data },
470	{ .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data },
471	{ .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data},
472	{ /* Sentinel */ },
473};
474MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
475
476static struct platform_driver imx95_bc_driver = {
477	.probe = imx95_bc_probe,
478	.driver = {
479		.name = "imx95-blk-ctl",
480		.of_match_table = imx95_bc_of_match,
481		.pm = &imx95_bc_pm_ops,
482	},
483};
484module_platform_driver(imx95_bc_driver);
485
486MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
487MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
488MODULE_LICENSE("GPL");