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