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 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
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,
248 .clk_reg_offset = 0,
249 };
250
251 static 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
274 static 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
280 static 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
293 static 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
imx95_bc_probe(struct platform_device * pdev)299 static 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
387 cleanup:
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
imx95_bc_runtime_suspend(struct device * dev)401 static 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
imx95_bc_runtime_resume(struct device * dev)409 static 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
imx95_bc_suspend(struct device * dev)418 static 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
imx95_bc_resume(struct device * dev)441 static 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
459 static 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
464 static 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 };
474 MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
475
476 static 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 };
484 module_platform_driver(imx95_bc_driver);
485
486 MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
487 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
488 MODULE_LICENSE("GPL");
489