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 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 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 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 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 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