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 25 enum { 26 CLK_GATE, 27 CLK_DIVIDER, 28 CLK_MUX, 29 }; 30 31 struct 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 }; 40 41 struct imx95_blk_ctl_clk_dev_data { 42 const char *name; 43 const char * const *parent_names; 44 u32 num_parents; 45 u32 reg; 46 u32 bit_idx; 47 u32 bit_width; 48 u32 clk_type; 49 u32 flags; 50 u32 flags2; 51 u32 type; 52 }; 53 54 struct imx95_blk_ctl_dev_data { 55 const struct imx95_blk_ctl_clk_dev_data *clk_dev_data; 56 u32 num_clks; 57 bool rpm_enabled; 58 u32 clk_reg_offset; 59 }; 60 61 static const struct imx95_blk_ctl_clk_dev_data vpublk_clk_dev_data[] = { 62 [IMX95_CLK_VPUBLK_WAVE] = { 63 .name = "vpublk_wave_vpu", 64 .parent_names = (const char *[]){ "vpu", }, 65 .num_parents = 1, 66 .reg = 8, 67 .bit_idx = 0, 68 .type = CLK_GATE, 69 .flags = CLK_SET_RATE_PARENT, 70 .flags2 = CLK_GATE_SET_TO_DISABLE, 71 }, 72 [IMX95_CLK_VPUBLK_JPEG_ENC] = { 73 .name = "vpublk_jpeg_enc", 74 .parent_names = (const char *[]){ "vpujpeg", }, 75 .num_parents = 1, 76 .reg = 8, 77 .bit_idx = 1, 78 .type = CLK_GATE, 79 .flags = CLK_SET_RATE_PARENT, 80 .flags2 = CLK_GATE_SET_TO_DISABLE, 81 }, 82 [IMX95_CLK_VPUBLK_JPEG_DEC] = { 83 .name = "vpublk_jpeg_dec", 84 .parent_names = (const char *[]){ "vpujpeg", }, 85 .num_parents = 1, 86 .reg = 8, 87 .bit_idx = 2, 88 .type = CLK_GATE, 89 .flags = CLK_SET_RATE_PARENT, 90 .flags2 = CLK_GATE_SET_TO_DISABLE, 91 } 92 }; 93 94 static const struct imx95_blk_ctl_dev_data vpublk_dev_data = { 95 .num_clks = ARRAY_SIZE(vpublk_clk_dev_data), 96 .clk_dev_data = vpublk_clk_dev_data, 97 .rpm_enabled = true, 98 .clk_reg_offset = 8, 99 }; 100 101 static const struct imx95_blk_ctl_clk_dev_data camblk_clk_dev_data[] = { 102 [IMX95_CLK_CAMBLK_CSI2_FOR0] = { 103 .name = "camblk_csi2_for0", 104 .parent_names = (const char *[]){ "camisi", }, 105 .num_parents = 1, 106 .reg = 0, 107 .bit_idx = 0, 108 .type = CLK_GATE, 109 .flags = CLK_SET_RATE_PARENT, 110 .flags2 = CLK_GATE_SET_TO_DISABLE, 111 }, 112 [IMX95_CLK_CAMBLK_CSI2_FOR1] = { 113 .name = "camblk_csi2_for1", 114 .parent_names = (const char *[]){ "camisi", }, 115 .num_parents = 1, 116 .reg = 0, 117 .bit_idx = 1, 118 .type = CLK_GATE, 119 .flags = CLK_SET_RATE_PARENT, 120 .flags2 = CLK_GATE_SET_TO_DISABLE, 121 }, 122 [IMX95_CLK_CAMBLK_ISP_AXI] = { 123 .name = "camblk_isp_axi", 124 .parent_names = (const char *[]){ "camaxi", }, 125 .num_parents = 1, 126 .reg = 0, 127 .bit_idx = 4, 128 .type = CLK_GATE, 129 .flags = CLK_SET_RATE_PARENT, 130 .flags2 = CLK_GATE_SET_TO_DISABLE, 131 }, 132 [IMX95_CLK_CAMBLK_ISP_PIXEL] = { 133 .name = "camblk_isp_pixel", 134 .parent_names = (const char *[]){ "camisi", }, 135 .num_parents = 1, 136 .reg = 0, 137 .bit_idx = 5, 138 .type = CLK_GATE, 139 .flags = CLK_SET_RATE_PARENT, 140 .flags2 = CLK_GATE_SET_TO_DISABLE, 141 }, 142 [IMX95_CLK_CAMBLK_ISP] = { 143 .name = "camblk_isp", 144 .parent_names = (const char *[]){ "camisi", }, 145 .num_parents = 1, 146 .reg = 0, 147 .bit_idx = 6, 148 .type = CLK_GATE, 149 .flags = CLK_SET_RATE_PARENT, 150 .flags2 = CLK_GATE_SET_TO_DISABLE, 151 } 152 }; 153 154 static const struct imx95_blk_ctl_dev_data camblk_dev_data = { 155 .num_clks = ARRAY_SIZE(camblk_clk_dev_data), 156 .clk_dev_data = camblk_clk_dev_data, 157 .clk_reg_offset = 0, 158 }; 159 160 static const struct imx95_blk_ctl_clk_dev_data imx95_lvds_clk_dev_data[] = { 161 [IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = { 162 .name = "ldb_phy_div", 163 .parent_names = (const char *[]){ "ldbpll", }, 164 .num_parents = 1, 165 .reg = 0, 166 .bit_idx = 0, 167 .bit_width = 1, 168 .type = CLK_DIVIDER, 169 .flags2 = CLK_DIVIDER_POWER_OF_TWO, 170 }, 171 [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = { 172 .name = "lvds_ch0_gate", 173 .parent_names = (const char *[]){ "ldb_phy_div", }, 174 .num_parents = 1, 175 .reg = 0, 176 .bit_idx = 1, 177 .bit_width = 1, 178 .type = CLK_GATE, 179 .flags = CLK_SET_RATE_PARENT, 180 .flags2 = CLK_GATE_SET_TO_DISABLE, 181 }, 182 [IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = { 183 .name = "lvds_ch1_gate", 184 .parent_names = (const char *[]){ "ldb_phy_div", }, 185 .num_parents = 1, 186 .reg = 0, 187 .bit_idx = 2, 188 .bit_width = 1, 189 .type = CLK_GATE, 190 .flags = CLK_SET_RATE_PARENT, 191 .flags2 = CLK_GATE_SET_TO_DISABLE, 192 }, 193 [IMX95_CLK_DISPMIX_PIX_DI0_GATE] = { 194 .name = "lvds_di0_gate", 195 .parent_names = (const char *[]){ "ldb_pll_div7", }, 196 .num_parents = 1, 197 .reg = 0, 198 .bit_idx = 3, 199 .bit_width = 1, 200 .type = CLK_GATE, 201 .flags = CLK_SET_RATE_PARENT, 202 .flags2 = CLK_GATE_SET_TO_DISABLE, 203 }, 204 [IMX95_CLK_DISPMIX_PIX_DI1_GATE] = { 205 .name = "lvds_di1_gate", 206 .parent_names = (const char *[]){ "ldb_pll_div7", }, 207 .num_parents = 1, 208 .reg = 0, 209 .bit_idx = 4, 210 .bit_width = 1, 211 .type = CLK_GATE, 212 .flags = CLK_SET_RATE_PARENT, 213 .flags2 = CLK_GATE_SET_TO_DISABLE, 214 }, 215 }; 216 217 static const struct imx95_blk_ctl_dev_data imx95_lvds_csr_dev_data = { 218 .num_clks = ARRAY_SIZE(imx95_lvds_clk_dev_data), 219 .clk_dev_data = imx95_lvds_clk_dev_data, 220 .clk_reg_offset = 0, 221 }; 222 223 static const char * const imx95_disp_engine_parents[] = { 224 "videopll1", "dsi_pll", "ldb_pll_div7" 225 }; 226 227 static const struct imx95_blk_ctl_clk_dev_data imx95_dispmix_csr_clk_dev_data[] = { 228 [IMX95_CLK_DISPMIX_ENG0_SEL] = { 229 .name = "disp_engine0_sel", 230 .parent_names = imx95_disp_engine_parents, 231 .num_parents = ARRAY_SIZE(imx95_disp_engine_parents), 232 .reg = 0, 233 .bit_idx = 0, 234 .bit_width = 2, 235 .type = CLK_MUX, 236 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 237 }, 238 [IMX95_CLK_DISPMIX_ENG1_SEL] = { 239 .name = "disp_engine1_sel", 240 .parent_names = imx95_disp_engine_parents, 241 .num_parents = ARRAY_SIZE(imx95_disp_engine_parents), 242 .reg = 0, 243 .bit_idx = 2, 244 .bit_width = 2, 245 .type = CLK_MUX, 246 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 247 } 248 }; 249 250 static const struct imx95_blk_ctl_dev_data imx95_dispmix_csr_dev_data = { 251 .num_clks = ARRAY_SIZE(imx95_dispmix_csr_clk_dev_data), 252 .clk_dev_data = imx95_dispmix_csr_clk_dev_data, 253 .clk_reg_offset = 0, 254 }; 255 256 static const struct imx95_blk_ctl_clk_dev_data netxmix_clk_dev_data[] = { 257 [IMX95_CLK_NETCMIX_ENETC0_RMII] = { 258 .name = "enetc0_rmii_sel", 259 .parent_names = (const char *[]){"ext_enetref", "enetref"}, 260 .num_parents = 2, 261 .reg = 4, 262 .bit_idx = 5, 263 .bit_width = 1, 264 .type = CLK_MUX, 265 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 266 }, 267 [IMX95_CLK_NETCMIX_ENETC1_RMII] = { 268 .name = "enetc1_rmii_sel", 269 .parent_names = (const char *[]){"ext_enetref", "enetref"}, 270 .num_parents = 2, 271 .reg = 4, 272 .bit_idx = 10, 273 .bit_width = 1, 274 .type = CLK_MUX, 275 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 276 }, 277 }; 278 279 static const struct imx95_blk_ctl_dev_data netcmix_dev_data = { 280 .num_clks = ARRAY_SIZE(netxmix_clk_dev_data), 281 .clk_dev_data = netxmix_clk_dev_data, 282 .clk_reg_offset = 0, 283 }; 284 285 static const struct imx95_blk_ctl_clk_dev_data hsio_blk_ctl_clk_dev_data[] = { 286 [0] = { 287 .name = "hsio_blk_ctl_clk", 288 .parent_names = (const char *[]){ "hsio_pll", }, 289 .num_parents = 1, 290 .reg = 0, 291 .bit_idx = 6, 292 .bit_width = 1, 293 .type = CLK_GATE, 294 .flags = CLK_SET_RATE_PARENT, 295 } 296 }; 297 298 static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = { 299 .num_clks = 1, 300 .clk_dev_data = hsio_blk_ctl_clk_dev_data, 301 .clk_reg_offset = 0, 302 }; 303 304 static const struct imx95_blk_ctl_clk_dev_data imx94_lvds_clk_dev_data[] = { 305 [IMX94_CLK_DISPMIX_LVDS_CLK_GATE] = { 306 .name = "lvds_clk_gate", 307 .parent_names = (const char *[]){ "ldbpll", }, 308 .num_parents = 1, 309 .reg = 0, 310 .bit_idx = 1, 311 .bit_width = 1, 312 .type = CLK_GATE, 313 .flags = CLK_SET_RATE_PARENT, 314 .flags2 = CLK_GATE_SET_TO_DISABLE, 315 }, 316 }; 317 318 static const struct imx95_blk_ctl_dev_data imx94_lvds_csr_dev_data = { 319 .num_clks = ARRAY_SIZE(imx94_lvds_clk_dev_data), 320 .clk_dev_data = imx94_lvds_clk_dev_data, 321 .clk_reg_offset = 0, 322 .rpm_enabled = true, 323 }; 324 325 static const char * const imx94_disp_engine_parents[] = { 326 "disppix", "ldb_pll_div7" 327 }; 328 329 static const struct imx95_blk_ctl_clk_dev_data imx94_dispmix_csr_clk_dev_data[] = { 330 [IMX94_CLK_DISPMIX_CLK_SEL] = { 331 .name = "disp_clk_sel", 332 .parent_names = imx94_disp_engine_parents, 333 .num_parents = ARRAY_SIZE(imx94_disp_engine_parents), 334 .reg = 0, 335 .bit_idx = 1, 336 .bit_width = 1, 337 .type = CLK_MUX, 338 .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 339 }, 340 }; 341 342 static const struct imx95_blk_ctl_dev_data imx94_dispmix_csr_dev_data = { 343 .num_clks = ARRAY_SIZE(imx94_dispmix_csr_clk_dev_data), 344 .clk_dev_data = imx94_dispmix_csr_clk_dev_data, 345 .clk_reg_offset = 0, 346 .rpm_enabled = true, 347 }; 348 349 static int imx95_bc_probe(struct platform_device *pdev) 350 { 351 struct device *dev = &pdev->dev; 352 const struct imx95_blk_ctl_dev_data *bc_data; 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_data = of_device_get_match_data(dev); 383 if (!bc_data) 384 return devm_of_platform_populate(dev); 385 386 clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc_data->num_clks), 387 GFP_KERNEL); 388 if (!clk_hw_data) 389 return -ENOMEM; 390 391 if (bc_data->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_data->num_clks; 397 hws = clk_hw_data->hws; 398 399 for (i = 0; i < bc_data->num_clks; i++) { 400 const struct imx95_blk_ctl_clk_dev_data *data = &bc_data->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 441 cleanup: 442 for (i = 0; i < bc_data->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 452 static int imx95_bc_runtime_suspend(struct device *dev) 453 { 454 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 455 456 clk_disable_unprepare(bc->clk_apb); 457 return 0; 458 } 459 460 static int imx95_bc_runtime_resume(struct device *dev) 461 { 462 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 463 464 return clk_prepare_enable(bc->clk_apb); 465 } 466 #endif 467 468 #ifdef CONFIG_PM_SLEEP 469 static int imx95_bc_suspend(struct device *dev) 470 { 471 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 472 const struct imx95_blk_ctl_dev_data *bc_data; 473 int ret; 474 475 bc_data = of_device_get_match_data(dev); 476 if (!bc_data) 477 return 0; 478 479 if (bc_data->rpm_enabled) { 480 ret = pm_runtime_get_sync(bc->dev); 481 if (ret < 0) { 482 pm_runtime_put_noidle(bc->dev); 483 return ret; 484 } 485 } 486 487 bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset); 488 489 return 0; 490 } 491 492 static int imx95_bc_resume(struct device *dev) 493 { 494 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 495 const struct imx95_blk_ctl_dev_data *bc_data; 496 497 bc_data = of_device_get_match_data(dev); 498 if (!bc_data) 499 return 0; 500 501 writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset); 502 503 if (bc_data->rpm_enabled) 504 pm_runtime_put(bc->dev); 505 506 return 0; 507 } 508 #endif 509 510 static const struct dev_pm_ops imx95_bc_pm_ops = { 511 SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL) 512 SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume) 513 }; 514 515 static const struct of_device_id imx95_bc_of_match[] = { 516 { .compatible = "nxp,imx94-display-csr", .data = &imx94_dispmix_csr_dev_data }, 517 { .compatible = "nxp,imx94-lvds-csr", .data = &imx94_lvds_csr_dev_data }, 518 { .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data }, 519 { .compatible = "nxp,imx95-display-master-csr", }, 520 { .compatible = "nxp,imx95-display-csr", .data = &imx95_dispmix_csr_dev_data }, 521 { .compatible = "nxp,imx95-lvds-csr", .data = &imx95_lvds_csr_dev_data }, 522 { .compatible = "nxp,imx95-hsio-blk-ctl", .data = &hsio_blk_ctl_dev_data }, 523 { .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data }, 524 { .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data}, 525 { /* Sentinel */ }, 526 }; 527 MODULE_DEVICE_TABLE(of, imx95_bc_of_match); 528 529 static struct platform_driver imx95_bc_driver = { 530 .probe = imx95_bc_probe, 531 .driver = { 532 .name = "imx95-blk-ctl", 533 .of_match_table = imx95_bc_of_match, 534 .pm = &imx95_bc_pm_ops, 535 }, 536 }; 537 module_platform_driver(imx95_bc_driver); 538 539 MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver"); 540 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 541 MODULE_LICENSE("GPL"); 542