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 int imx95_bc_probe(struct platform_device *pdev) 281 { 282 struct device *dev = &pdev->dev; 283 const struct imx95_blk_ctl_dev_data *bc_data; 284 struct imx95_blk_ctl *bc; 285 struct clk_hw_onecell_data *clk_hw_data; 286 struct clk_hw **hws; 287 void __iomem *base; 288 int i, ret; 289 290 bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); 291 if (!bc) 292 return -ENOMEM; 293 bc->dev = dev; 294 dev_set_drvdata(&pdev->dev, bc); 295 296 spin_lock_init(&bc->lock); 297 298 base = devm_platform_ioremap_resource(pdev, 0); 299 if (IS_ERR(base)) 300 return PTR_ERR(base); 301 302 bc->base = base; 303 bc->clk_apb = devm_clk_get(dev, NULL); 304 if (IS_ERR(bc->clk_apb)) 305 return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n"); 306 307 ret = clk_prepare_enable(bc->clk_apb); 308 if (ret) { 309 dev_err(dev, "failed to enable apb clock: %d\n", ret); 310 return ret; 311 } 312 313 bc_data = of_device_get_match_data(dev); 314 if (!bc_data) 315 return devm_of_platform_populate(dev); 316 317 clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc_data->num_clks), 318 GFP_KERNEL); 319 if (!clk_hw_data) 320 return -ENOMEM; 321 322 if (bc_data->rpm_enabled) 323 pm_runtime_enable(&pdev->dev); 324 325 clk_hw_data->num = bc_data->num_clks; 326 hws = clk_hw_data->hws; 327 328 for (i = 0; i < bc_data->num_clks; i++) { 329 const struct imx95_blk_ctl_clk_dev_data *data = &bc_data->clk_dev_data[i]; 330 void __iomem *reg = base + data->reg; 331 332 if (data->type == CLK_MUX) { 333 hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names, 334 data->num_parents, data->flags, reg, 335 data->bit_idx, data->bit_width, 336 data->flags2, &bc->lock); 337 } else if (data->type == CLK_DIVIDER) { 338 hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0], 339 data->flags, reg, data->bit_idx, 340 data->bit_width, data->flags2, &bc->lock); 341 } else { 342 hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0], 343 data->flags, reg, data->bit_idx, 344 data->flags2, &bc->lock); 345 } 346 if (IS_ERR(hws[i])) { 347 ret = PTR_ERR(hws[i]); 348 dev_err(dev, "failed to register: %s:%d\n", data->name, ret); 349 goto cleanup; 350 } 351 } 352 353 ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data); 354 if (ret) 355 goto cleanup; 356 357 ret = devm_of_platform_populate(dev); 358 if (ret) { 359 of_clk_del_provider(dev->of_node); 360 goto cleanup; 361 } 362 363 if (pm_runtime_enabled(bc->dev)) 364 clk_disable_unprepare(bc->clk_apb); 365 366 return 0; 367 368 cleanup: 369 for (i = 0; i < bc_data->num_clks; i++) { 370 if (IS_ERR_OR_NULL(hws[i])) 371 continue; 372 clk_hw_unregister(hws[i]); 373 } 374 375 if (bc_data->rpm_enabled) 376 pm_runtime_disable(&pdev->dev); 377 378 return ret; 379 } 380 381 #ifdef CONFIG_PM 382 static int imx95_bc_runtime_suspend(struct device *dev) 383 { 384 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 385 386 clk_disable_unprepare(bc->clk_apb); 387 return 0; 388 } 389 390 static int imx95_bc_runtime_resume(struct device *dev) 391 { 392 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 393 394 return clk_prepare_enable(bc->clk_apb); 395 } 396 #endif 397 398 #ifdef CONFIG_PM_SLEEP 399 static int imx95_bc_suspend(struct device *dev) 400 { 401 struct imx95_blk_ctl *bc = dev_get_drvdata(dev); 402 const struct imx95_blk_ctl_dev_data *bc_data; 403 int ret; 404 405 bc_data = of_device_get_match_data(dev); 406 if (!bc_data) 407 return 0; 408 409 if (bc_data->rpm_enabled) { 410 ret = pm_runtime_get_sync(bc->dev); 411 if (ret < 0) { 412 pm_runtime_put_noidle(bc->dev); 413 return ret; 414 } 415 } 416 417 bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset); 418 419 return 0; 420 } 421 422 static int imx95_bc_resume(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 427 bc_data = of_device_get_match_data(dev); 428 if (!bc_data) 429 return 0; 430 431 writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset); 432 433 if (bc_data->rpm_enabled) 434 pm_runtime_put(bc->dev); 435 436 return 0; 437 } 438 #endif 439 440 static const struct dev_pm_ops imx95_bc_pm_ops = { 441 SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL) 442 SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume) 443 }; 444 445 static const struct of_device_id imx95_bc_of_match[] = { 446 { .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data }, 447 { .compatible = "nxp,imx95-display-master-csr", }, 448 { .compatible = "nxp,imx95-lvds-csr", .data = &lvds_csr_dev_data }, 449 { .compatible = "nxp,imx95-display-csr", .data = &dispmix_csr_dev_data }, 450 { .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data }, 451 { .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data}, 452 { /* Sentinel */ }, 453 }; 454 MODULE_DEVICE_TABLE(of, imx95_bc_of_match); 455 456 static struct platform_driver imx95_bc_driver = { 457 .probe = imx95_bc_probe, 458 .driver = { 459 .name = "imx95-blk-ctl", 460 .of_match_table = imx95_bc_of_match, 461 .pm = &imx95_bc_pm_ops, 462 }, 463 }; 464 module_platform_driver(imx95_bc_driver); 465 466 MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver"); 467 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 468 MODULE_LICENSE("GPL"); 469