1 // SPDX-License-Identifier: GPL-2.0
6 #include <dt-bindings/clock/nxp,imx95-clock.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>
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>
30 struct imx95_blk_ctl {
36 /* clock gate register */
40 struct imx95_blk_ctl_clk_dev_data {
42 const char * const *parent_names;
53 struct imx95_blk_ctl_dev_data {
54 const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;
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", },
68 .flags = CLK_SET_RATE_PARENT,
69 .flags2 = CLK_GATE_SET_TO_DISABLE,
71 [IMX95_CLK_VPUBLK_JPEG_ENC] = {
72 .name = "vpublk_jpeg_enc",
73 .parent_names = (const char *[]){ "vpujpeg", },
78 .flags = CLK_SET_RATE_PARENT,
79 .flags2 = CLK_GATE_SET_TO_DISABLE,
81 [IMX95_CLK_VPUBLK_JPEG_DEC] = {
82 .name = "vpublk_jpeg_dec",
83 .parent_names = (const char *[]){ "vpujpeg", },
88 .flags = CLK_SET_RATE_PARENT,
89 .flags2 = CLK_GATE_SET_TO_DISABLE,
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,
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", },
108 .flags = CLK_SET_RATE_PARENT,
109 .flags2 = CLK_GATE_SET_TO_DISABLE,
111 [IMX95_CLK_CAMBLK_CSI2_FOR1] = {
112 .name = "camblk_csi2_for1",
113 .parent_names = (const char *[]){ "camisi", },
118 .flags = CLK_SET_RATE_PARENT,
119 .flags2 = CLK_GATE_SET_TO_DISABLE,
121 [IMX95_CLK_CAMBLK_ISP_AXI] = {
122 .name = "camblk_isp_axi",
123 .parent_names = (const char *[]){ "camaxi", },
128 .flags = CLK_SET_RATE_PARENT,
129 .flags2 = CLK_GATE_SET_TO_DISABLE,
131 [IMX95_CLK_CAMBLK_ISP_PIXEL] = {
132 .name = "camblk_isp_pixel",
133 .parent_names = (const char *[]){ "camisi", },
138 .flags = CLK_SET_RATE_PARENT,
139 .flags2 = CLK_GATE_SET_TO_DISABLE,
141 [IMX95_CLK_CAMBLK_ISP] = {
142 .name = "camblk_isp",
143 .parent_names = (const char *[]){ "camisi", },
148 .flags = CLK_SET_RATE_PARENT,
149 .flags2 = CLK_GATE_SET_TO_DISABLE,
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,
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", },
168 .flags2 = CLK_DIVIDER_POWER_OF_TWO,
170 [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {
171 .name = "lvds_ch0_gate",
172 .parent_names = (const char *[]){ "ldb_phy_div", },
178 .flags = CLK_SET_RATE_PARENT,
179 .flags2 = CLK_GATE_SET_TO_DISABLE,
181 [IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = {
182 .name = "lvds_ch1_gate",
183 .parent_names = (const char *[]){ "ldb_phy_div", },
189 .flags = CLK_SET_RATE_PARENT,
190 .flags2 = CLK_GATE_SET_TO_DISABLE,
192 [IMX95_CLK_DISPMIX_PIX_DI0_GATE] = {
193 .name = "lvds_di0_gate",
194 .parent_names = (const char *[]){ "ldb_pll_div7", },
200 .flags = CLK_SET_RATE_PARENT,
201 .flags2 = CLK_GATE_SET_TO_DISABLE,
203 [IMX95_CLK_DISPMIX_PIX_DI1_GATE] = {
204 .name = "lvds_di1_gate",
205 .parent_names = (const char *[]){ "ldb_pll_div7", },
211 .flags = CLK_SET_RATE_PARENT,
212 .flags2 = CLK_GATE_SET_TO_DISABLE,
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,
222 static 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", },
231 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
233 [IMX95_CLK_DISPMIX_ENG1_SEL] = {
234 .name = "disp_engine1_sel",
235 .parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },
241 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
245 static 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,
251 static int imx95_bc_probe(struct platform_device *pdev)
253 struct device *dev = &pdev->dev;
254 const struct imx95_blk_ctl_dev_data *bc_data;
255 struct imx95_blk_ctl *bc;
256 struct clk_hw_onecell_data *clk_hw_data;
261 bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
265 dev_set_drvdata(&pdev->dev, bc);
267 spin_lock_init(&bc->lock);
269 base = devm_platform_ioremap_resource(pdev, 0);
271 return PTR_ERR(base);
274 bc->clk_apb = devm_clk_get(dev, NULL);
275 if (IS_ERR(bc->clk_apb))
276 return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n");
278 ret = clk_prepare_enable(bc->clk_apb);
280 dev_err(dev, "failed to enable apb clock: %d\n", ret);
284 bc_data = of_device_get_match_data(dev);
286 return devm_of_platform_populate(dev);
288 clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc_data->num_clks),
293 if (bc_data->rpm_enabled)
294 pm_runtime_enable(&pdev->dev);
296 clk_hw_data->num = bc_data->num_clks;
297 hws = clk_hw_data->hws;
299 for (i = 0; i < bc_data->num_clks; i++) {
300 const struct imx95_blk_ctl_clk_dev_data *data = &bc_data->clk_dev_data[i];
301 void __iomem *reg = base + data->reg;
303 if (data->type == CLK_MUX) {
304 hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names,
305 data->num_parents, data->flags, reg,
306 data->bit_idx, data->bit_width,
307 data->flags2, &bc->lock);
308 } else if (data->type == CLK_DIVIDER) {
309 hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0],
310 data->flags, reg, data->bit_idx,
311 data->bit_width, data->flags2, &bc->lock);
313 hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0],
314 data->flags, reg, data->bit_idx,
315 data->flags2, &bc->lock);
317 if (IS_ERR(hws[i])) {
318 ret = PTR_ERR(hws[i]);
319 dev_err(dev, "failed to register: %s:%d\n", data->name, ret);
324 ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data);
328 ret = devm_of_platform_populate(dev);
330 of_clk_del_provider(dev->of_node);
334 if (pm_runtime_enabled(bc->dev))
335 clk_disable_unprepare(bc->clk_apb);
340 for (i = 0; i < bc_data->num_clks; i++) {
341 if (IS_ERR_OR_NULL(hws[i]))
343 clk_hw_unregister(hws[i]);
346 if (bc_data->rpm_enabled)
347 pm_runtime_disable(&pdev->dev);
353 static int imx95_bc_runtime_suspend(struct device *dev)
355 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
357 clk_disable_unprepare(bc->clk_apb);
361 static int imx95_bc_runtime_resume(struct device *dev)
363 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
365 return clk_prepare_enable(bc->clk_apb);
369 #ifdef CONFIG_PM_SLEEP
370 static int imx95_bc_suspend(struct device *dev)
372 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
373 const struct imx95_blk_ctl_dev_data *bc_data;
376 bc_data = of_device_get_match_data(dev);
380 if (bc_data->rpm_enabled) {
381 ret = pm_runtime_get_sync(bc->dev);
383 pm_runtime_put_noidle(bc->dev);
388 bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
393 static int imx95_bc_resume(struct device *dev)
395 struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
396 const struct imx95_blk_ctl_dev_data *bc_data;
398 bc_data = of_device_get_match_data(dev);
402 writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
404 if (bc_data->rpm_enabled)
405 pm_runtime_put(bc->dev);
411 static const struct dev_pm_ops imx95_bc_pm_ops = {
412 SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL)
413 SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume)
416 static const struct of_device_id imx95_bc_of_match[] = {
417 { .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data },
418 { .compatible = "nxp,imx95-display-master-csr", },
419 { .compatible = "nxp,imx95-lvds-csr", .data = &lvds_csr_dev_data },
420 { .compatible = "nxp,imx95-display-csr", .data = &dispmix_csr_dev_data },
421 { .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data },
424 MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
426 static struct platform_driver imx95_bc_driver = {
427 .probe = imx95_bc_probe,
429 .name = "imx95-blk-ctl",
430 .of_match_table = imx95_bc_of_match,
431 .pm = &imx95_bc_pm_ops,
434 module_platform_driver(imx95_bc_driver);
436 MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
438 MODULE_LICENSE("GPL");