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