imx8m-blk-ctrl.c (22745B)
1// SPDX-License-Identifier: GPL-2.0+ 2 3/* 4 * Copyright 2021 Pengutronix, Lucas Stach <kernel@pengutronix.de> 5 */ 6 7#include <linux/device.h> 8#include <linux/module.h> 9#include <linux/of_device.h> 10#include <linux/platform_device.h> 11#include <linux/pm_domain.h> 12#include <linux/pm_runtime.h> 13#include <linux/regmap.h> 14#include <linux/clk.h> 15 16#include <dt-bindings/power/imx8mm-power.h> 17#include <dt-bindings/power/imx8mn-power.h> 18#include <dt-bindings/power/imx8mp-power.h> 19#include <dt-bindings/power/imx8mq-power.h> 20 21#define BLK_SFT_RSTN 0x0 22#define BLK_CLK_EN 0x4 23#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */ 24 25struct imx8m_blk_ctrl_domain; 26 27struct imx8m_blk_ctrl { 28 struct device *dev; 29 struct notifier_block power_nb; 30 struct device *bus_power_dev; 31 struct regmap *regmap; 32 struct imx8m_blk_ctrl_domain *domains; 33 struct genpd_onecell_data onecell_data; 34}; 35 36struct imx8m_blk_ctrl_domain_data { 37 const char *name; 38 const char * const *clk_names; 39 int num_clks; 40 const char *gpc_name; 41 u32 rst_mask; 42 u32 clk_mask; 43 44 /* 45 * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register 46 * which is used to control the reset for the MIPI Phy. 47 * Since it's only present in certain circumstances, 48 * an if-statement should be used before setting and clearing this 49 * register. 50 */ 51 u32 mipi_phy_rst_mask; 52}; 53 54#define DOMAIN_MAX_CLKS 4 55 56struct imx8m_blk_ctrl_domain { 57 struct generic_pm_domain genpd; 58 const struct imx8m_blk_ctrl_domain_data *data; 59 struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; 60 struct device *power_dev; 61 struct imx8m_blk_ctrl *bc; 62}; 63 64struct imx8m_blk_ctrl_data { 65 int max_reg; 66 notifier_fn_t power_notifier_fn; 67 const struct imx8m_blk_ctrl_domain_data *domains; 68 int num_domains; 69}; 70 71static inline struct imx8m_blk_ctrl_domain * 72to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd) 73{ 74 return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd); 75} 76 77static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) 78{ 79 struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd); 80 const struct imx8m_blk_ctrl_domain_data *data = domain->data; 81 struct imx8m_blk_ctrl *bc = domain->bc; 82 int ret; 83 84 /* make sure bus domain is awake */ 85 ret = pm_runtime_get_sync(bc->bus_power_dev); 86 if (ret < 0) { 87 pm_runtime_put_noidle(bc->bus_power_dev); 88 dev_err(bc->dev, "failed to power up bus domain\n"); 89 return ret; 90 } 91 92 /* put devices into reset */ 93 regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); 94 if (data->mipi_phy_rst_mask) 95 regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); 96 97 /* enable upstream and blk-ctrl clocks to allow reset to propagate */ 98 ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); 99 if (ret) { 100 dev_err(bc->dev, "failed to enable clocks\n"); 101 goto bus_put; 102 } 103 regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); 104 105 /* power up upstream GPC domain */ 106 ret = pm_runtime_get_sync(domain->power_dev); 107 if (ret < 0) { 108 dev_err(bc->dev, "failed to power up peripheral domain\n"); 109 goto clk_disable; 110 } 111 112 /* wait for reset to propagate */ 113 udelay(5); 114 115 /* release reset */ 116 regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); 117 if (data->mipi_phy_rst_mask) 118 regmap_set_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); 119 120 /* disable upstream clocks */ 121 clk_bulk_disable_unprepare(data->num_clks, domain->clks); 122 123 return 0; 124 125clk_disable: 126 clk_bulk_disable_unprepare(data->num_clks, domain->clks); 127bus_put: 128 pm_runtime_put(bc->bus_power_dev); 129 130 return ret; 131} 132 133static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd) 134{ 135 struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd); 136 const struct imx8m_blk_ctrl_domain_data *data = domain->data; 137 struct imx8m_blk_ctrl *bc = domain->bc; 138 139 /* put devices into reset and disable clocks */ 140 if (data->mipi_phy_rst_mask) 141 regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); 142 143 regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); 144 regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); 145 146 /* power down upstream GPC domain */ 147 pm_runtime_put(domain->power_dev); 148 149 /* allow bus domain to suspend */ 150 pm_runtime_put(bc->bus_power_dev); 151 152 return 0; 153} 154 155static struct generic_pm_domain * 156imx8m_blk_ctrl_xlate(struct of_phandle_args *args, void *data) 157{ 158 struct genpd_onecell_data *onecell_data = data; 159 unsigned int index = args->args[0]; 160 161 if (args->args_count != 1 || 162 index >= onecell_data->num_domains) 163 return ERR_PTR(-EINVAL); 164 165 return onecell_data->domains[index]; 166} 167 168static struct lock_class_key blk_ctrl_genpd_lock_class; 169 170static int imx8m_blk_ctrl_probe(struct platform_device *pdev) 171{ 172 const struct imx8m_blk_ctrl_data *bc_data; 173 struct device *dev = &pdev->dev; 174 struct imx8m_blk_ctrl *bc; 175 void __iomem *base; 176 int i, ret; 177 178 struct regmap_config regmap_config = { 179 .reg_bits = 32, 180 .val_bits = 32, 181 .reg_stride = 4, 182 }; 183 184 bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); 185 if (!bc) 186 return -ENOMEM; 187 188 bc->dev = dev; 189 190 bc_data = of_device_get_match_data(dev); 191 192 base = devm_platform_ioremap_resource(pdev, 0); 193 if (IS_ERR(base)) 194 return PTR_ERR(base); 195 196 regmap_config.max_register = bc_data->max_reg; 197 bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); 198 if (IS_ERR(bc->regmap)) 199 return dev_err_probe(dev, PTR_ERR(bc->regmap), 200 "failed to init regmap\n"); 201 202 bc->domains = devm_kcalloc(dev, bc_data->num_domains, 203 sizeof(struct imx8m_blk_ctrl_domain), 204 GFP_KERNEL); 205 if (!bc->domains) 206 return -ENOMEM; 207 208 bc->onecell_data.num_domains = bc_data->num_domains; 209 bc->onecell_data.xlate = imx8m_blk_ctrl_xlate; 210 bc->onecell_data.domains = 211 devm_kcalloc(dev, bc_data->num_domains, 212 sizeof(struct generic_pm_domain *), GFP_KERNEL); 213 if (!bc->onecell_data.domains) 214 return -ENOMEM; 215 216 bc->bus_power_dev = genpd_dev_pm_attach_by_name(dev, "bus"); 217 if (IS_ERR(bc->bus_power_dev)) 218 return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), 219 "failed to attach power domain\n"); 220 221 for (i = 0; i < bc_data->num_domains; i++) { 222 const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i]; 223 struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; 224 int j; 225 226 domain->data = data; 227 228 for (j = 0; j < data->num_clks; j++) 229 domain->clks[j].id = data->clk_names[j]; 230 231 ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); 232 if (ret) { 233 dev_err_probe(dev, ret, "failed to get clock\n"); 234 goto cleanup_pds; 235 } 236 237 domain->power_dev = 238 dev_pm_domain_attach_by_name(dev, data->gpc_name); 239 if (IS_ERR(domain->power_dev)) { 240 dev_err_probe(dev, PTR_ERR(domain->power_dev), 241 "failed to attach power domain\n"); 242 ret = PTR_ERR(domain->power_dev); 243 goto cleanup_pds; 244 } 245 dev_set_name(domain->power_dev, "%s", data->name); 246 247 domain->genpd.name = data->name; 248 domain->genpd.power_on = imx8m_blk_ctrl_power_on; 249 domain->genpd.power_off = imx8m_blk_ctrl_power_off; 250 domain->bc = bc; 251 252 ret = pm_genpd_init(&domain->genpd, NULL, true); 253 if (ret) { 254 dev_err_probe(dev, ret, "failed to init power domain\n"); 255 dev_pm_domain_detach(domain->power_dev, true); 256 goto cleanup_pds; 257 } 258 259 /* 260 * We use runtime PM to trigger power on/off of the upstream GPC 261 * domain, as a strict hierarchical parent/child power domain 262 * setup doesn't allow us to meet the sequencing requirements. 263 * This means we have nested locking of genpd locks, without the 264 * nesting being visible at the genpd level, so we need a 265 * separate lock class to make lockdep aware of the fact that 266 * this are separate domain locks that can be nested without a 267 * self-deadlock. 268 */ 269 lockdep_set_class(&domain->genpd.mlock, 270 &blk_ctrl_genpd_lock_class); 271 272 bc->onecell_data.domains[i] = &domain->genpd; 273 } 274 275 ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); 276 if (ret) { 277 dev_err_probe(dev, ret, "failed to add power domain provider\n"); 278 goto cleanup_pds; 279 } 280 281 bc->power_nb.notifier_call = bc_data->power_notifier_fn; 282 ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); 283 if (ret) { 284 dev_err_probe(dev, ret, "failed to add power notifier\n"); 285 goto cleanup_provider; 286 } 287 288 dev_set_drvdata(dev, bc); 289 290 return 0; 291 292cleanup_provider: 293 of_genpd_del_provider(dev->of_node); 294cleanup_pds: 295 for (i--; i >= 0; i--) { 296 pm_genpd_remove(&bc->domains[i].genpd); 297 dev_pm_domain_detach(bc->domains[i].power_dev, true); 298 } 299 300 dev_pm_domain_detach(bc->bus_power_dev, true); 301 302 return ret; 303} 304 305static int imx8m_blk_ctrl_remove(struct platform_device *pdev) 306{ 307 struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); 308 int i; 309 310 of_genpd_del_provider(pdev->dev.of_node); 311 312 for (i = 0; bc->onecell_data.num_domains; i++) { 313 struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; 314 315 pm_genpd_remove(&domain->genpd); 316 dev_pm_domain_detach(domain->power_dev, true); 317 } 318 319 dev_pm_genpd_remove_notifier(bc->bus_power_dev); 320 321 dev_pm_domain_detach(bc->bus_power_dev, true); 322 323 return 0; 324} 325 326#ifdef CONFIG_PM_SLEEP 327static int imx8m_blk_ctrl_suspend(struct device *dev) 328{ 329 struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev); 330 int ret, i; 331 332 /* 333 * This may look strange, but is done so the generic PM_SLEEP code 334 * can power down our domains and more importantly power them up again 335 * after resume, without tripping over our usage of runtime PM to 336 * control the upstream GPC domains. Things happen in the right order 337 * in the system suspend/resume paths due to the device parent/child 338 * hierarchy. 339 */ 340 ret = pm_runtime_get_sync(bc->bus_power_dev); 341 if (ret < 0) { 342 pm_runtime_put_noidle(bc->bus_power_dev); 343 return ret; 344 } 345 346 for (i = 0; i < bc->onecell_data.num_domains; i++) { 347 struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; 348 349 ret = pm_runtime_get_sync(domain->power_dev); 350 if (ret < 0) { 351 pm_runtime_put_noidle(domain->power_dev); 352 goto out_fail; 353 } 354 } 355 356 return 0; 357 358out_fail: 359 for (i--; i >= 0; i--) 360 pm_runtime_put(bc->domains[i].power_dev); 361 362 pm_runtime_put(bc->bus_power_dev); 363 364 return ret; 365} 366 367static int imx8m_blk_ctrl_resume(struct device *dev) 368{ 369 struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev); 370 int i; 371 372 for (i = 0; i < bc->onecell_data.num_domains; i++) 373 pm_runtime_put(bc->domains[i].power_dev); 374 375 pm_runtime_put(bc->bus_power_dev); 376 377 return 0; 378} 379#endif 380 381static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = { 382 SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume) 383}; 384 385static int imx8mm_vpu_power_notifier(struct notifier_block *nb, 386 unsigned long action, void *data) 387{ 388 struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, 389 power_nb); 390 391 if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) 392 return NOTIFY_OK; 393 394 /* 395 * The ADB in the VPUMIX domain has no separate reset and clock 396 * enable bits, but is ungated together with the VPU clocks. To 397 * allow the handshake with the GPC to progress we put the VPUs 398 * in reset and ungate the clocks. 399 */ 400 regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2)); 401 regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2)); 402 403 if (action == GENPD_NOTIFY_ON) { 404 /* 405 * On power up we have no software backchannel to the GPC to 406 * wait for the ADB handshake to happen, so we just delay for a 407 * bit. On power down the GPC driver waits for the handshake. 408 */ 409 udelay(5); 410 411 /* set "fuse" bits to enable the VPUs */ 412 regmap_set_bits(bc->regmap, 0x8, 0xffffffff); 413 regmap_set_bits(bc->regmap, 0xc, 0xffffffff); 414 regmap_set_bits(bc->regmap, 0x10, 0xffffffff); 415 regmap_set_bits(bc->regmap, 0x14, 0xffffffff); 416 } 417 418 return NOTIFY_OK; 419} 420 421static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = { 422 [IMX8MM_VPUBLK_PD_G1] = { 423 .name = "vpublk-g1", 424 .clk_names = (const char *[]){ "g1", }, 425 .num_clks = 1, 426 .gpc_name = "g1", 427 .rst_mask = BIT(1), 428 .clk_mask = BIT(1), 429 }, 430 [IMX8MM_VPUBLK_PD_G2] = { 431 .name = "vpublk-g2", 432 .clk_names = (const char *[]){ "g2", }, 433 .num_clks = 1, 434 .gpc_name = "g2", 435 .rst_mask = BIT(0), 436 .clk_mask = BIT(0), 437 }, 438 [IMX8MM_VPUBLK_PD_H1] = { 439 .name = "vpublk-h1", 440 .clk_names = (const char *[]){ "h1", }, 441 .num_clks = 1, 442 .gpc_name = "h1", 443 .rst_mask = BIT(2), 444 .clk_mask = BIT(2), 445 }, 446}; 447 448static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = { 449 .max_reg = 0x18, 450 .power_notifier_fn = imx8mm_vpu_power_notifier, 451 .domains = imx8mm_vpu_blk_ctl_domain_data, 452 .num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data), 453}; 454 455static int imx8mm_disp_power_notifier(struct notifier_block *nb, 456 unsigned long action, void *data) 457{ 458 struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, 459 power_nb); 460 461 if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) 462 return NOTIFY_OK; 463 464 /* Enable bus clock and deassert bus reset */ 465 regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12)); 466 regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6)); 467 468 /* 469 * On power up we have no software backchannel to the GPC to 470 * wait for the ADB handshake to happen, so we just delay for a 471 * bit. On power down the GPC driver waits for the handshake. 472 */ 473 if (action == GENPD_NOTIFY_ON) 474 udelay(5); 475 476 477 return NOTIFY_OK; 478} 479 480static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = { 481 [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = { 482 .name = "dispblk-csi-bridge", 483 .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb", 484 "csi-bridge-core", }, 485 .num_clks = 3, 486 .gpc_name = "csi-bridge", 487 .rst_mask = BIT(0) | BIT(1) | BIT(2), 488 .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5), 489 }, 490 [IMX8MM_DISPBLK_PD_LCDIF] = { 491 .name = "dispblk-lcdif", 492 .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, 493 .num_clks = 3, 494 .gpc_name = "lcdif", 495 .clk_mask = BIT(6) | BIT(7), 496 }, 497 [IMX8MM_DISPBLK_PD_MIPI_DSI] = { 498 .name = "dispblk-mipi-dsi", 499 .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, 500 .num_clks = 2, 501 .gpc_name = "mipi-dsi", 502 .rst_mask = BIT(5), 503 .clk_mask = BIT(8) | BIT(9), 504 .mipi_phy_rst_mask = BIT(17), 505 }, 506 [IMX8MM_DISPBLK_PD_MIPI_CSI] = { 507 .name = "dispblk-mipi-csi", 508 .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, 509 .num_clks = 2, 510 .gpc_name = "mipi-csi", 511 .rst_mask = BIT(3) | BIT(4), 512 .clk_mask = BIT(10) | BIT(11), 513 .mipi_phy_rst_mask = BIT(16), 514 }, 515}; 516 517static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = { 518 .max_reg = 0x2c, 519 .power_notifier_fn = imx8mm_disp_power_notifier, 520 .domains = imx8mm_disp_blk_ctl_domain_data, 521 .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data), 522}; 523 524 525static int imx8mn_disp_power_notifier(struct notifier_block *nb, 526 unsigned long action, void *data) 527{ 528 struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, 529 power_nb); 530 531 if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) 532 return NOTIFY_OK; 533 534 /* Enable bus clock and deassert bus reset */ 535 regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); 536 regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); 537 538 /* 539 * On power up we have no software backchannel to the GPC to 540 * wait for the ADB handshake to happen, so we just delay for a 541 * bit. On power down the GPC driver waits for the handshake. 542 */ 543 if (action == GENPD_NOTIFY_ON) 544 udelay(5); 545 546 547 return NOTIFY_OK; 548} 549 550static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = { 551 [IMX8MN_DISPBLK_PD_MIPI_DSI] = { 552 .name = "dispblk-mipi-dsi", 553 .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, 554 .num_clks = 2, 555 .gpc_name = "mipi-dsi", 556 .rst_mask = BIT(0) | BIT(1), 557 .clk_mask = BIT(0) | BIT(1), 558 .mipi_phy_rst_mask = BIT(17), 559 }, 560 [IMX8MN_DISPBLK_PD_MIPI_CSI] = { 561 .name = "dispblk-mipi-csi", 562 .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, 563 .num_clks = 2, 564 .gpc_name = "mipi-csi", 565 .rst_mask = BIT(2) | BIT(3), 566 .clk_mask = BIT(2) | BIT(3), 567 .mipi_phy_rst_mask = BIT(16), 568 }, 569 [IMX8MN_DISPBLK_PD_LCDIF] = { 570 .name = "dispblk-lcdif", 571 .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, 572 .num_clks = 3, 573 .gpc_name = "lcdif", 574 .rst_mask = BIT(4) | BIT(5), 575 .clk_mask = BIT(4) | BIT(5), 576 }, 577 [IMX8MN_DISPBLK_PD_ISI] = { 578 .name = "dispblk-isi", 579 .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root", 580 "disp_apb_root"}, 581 .num_clks = 4, 582 .gpc_name = "isi", 583 .rst_mask = BIT(6) | BIT(7), 584 .clk_mask = BIT(6) | BIT(7), 585 }, 586}; 587 588static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { 589 .max_reg = 0x84, 590 .power_notifier_fn = imx8mn_disp_power_notifier, 591 .domains = imx8mn_disp_blk_ctl_domain_data, 592 .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data), 593}; 594 595static int imx8mp_media_power_notifier(struct notifier_block *nb, 596 unsigned long action, void *data) 597{ 598 struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, 599 power_nb); 600 601 if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) 602 return NOTIFY_OK; 603 604 /* Enable bus clock and deassert bus reset */ 605 regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); 606 regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); 607 608 /* 609 * On power up we have no software backchannel to the GPC to 610 * wait for the ADB handshake to happen, so we just delay for a 611 * bit. On power down the GPC driver waits for the handshake. 612 */ 613 if (action == GENPD_NOTIFY_ON) 614 udelay(5); 615 616 return NOTIFY_OK; 617} 618 619/* 620 * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1, 621 * section 13.2.2, 13.2.3 622 * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks 623 */ 624static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = { 625 [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = { 626 .name = "mediablk-mipi-dsi-1", 627 .clk_names = (const char *[]){ "apb", "phy", }, 628 .num_clks = 2, 629 .gpc_name = "mipi-dsi1", 630 .rst_mask = BIT(0) | BIT(1), 631 .clk_mask = BIT(0) | BIT(1), 632 .mipi_phy_rst_mask = BIT(17), 633 }, 634 [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = { 635 .name = "mediablk-mipi-csi2-1", 636 .clk_names = (const char *[]){ "apb", "cam1" }, 637 .num_clks = 2, 638 .gpc_name = "mipi-csi1", 639 .rst_mask = BIT(2) | BIT(3), 640 .clk_mask = BIT(2) | BIT(3), 641 .mipi_phy_rst_mask = BIT(16), 642 }, 643 [IMX8MP_MEDIABLK_PD_LCDIF_1] = { 644 .name = "mediablk-lcdif-1", 645 .clk_names = (const char *[]){ "disp1", "apb", "axi", }, 646 .num_clks = 3, 647 .gpc_name = "lcdif1", 648 .rst_mask = BIT(4) | BIT(5) | BIT(23), 649 .clk_mask = BIT(4) | BIT(5) | BIT(23), 650 }, 651 [IMX8MP_MEDIABLK_PD_ISI] = { 652 .name = "mediablk-isi", 653 .clk_names = (const char *[]){ "axi", "apb" }, 654 .num_clks = 2, 655 .gpc_name = "isi", 656 .rst_mask = BIT(6) | BIT(7), 657 .clk_mask = BIT(6) | BIT(7), 658 }, 659 [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = { 660 .name = "mediablk-mipi-csi2-2", 661 .clk_names = (const char *[]){ "apb", "cam2" }, 662 .num_clks = 2, 663 .gpc_name = "mipi-csi2", 664 .rst_mask = BIT(9) | BIT(10), 665 .clk_mask = BIT(9) | BIT(10), 666 .mipi_phy_rst_mask = BIT(30), 667 }, 668 [IMX8MP_MEDIABLK_PD_LCDIF_2] = { 669 .name = "mediablk-lcdif-2", 670 .clk_names = (const char *[]){ "disp2", "apb", "axi", }, 671 .num_clks = 3, 672 .gpc_name = "lcdif2", 673 .rst_mask = BIT(11) | BIT(12) | BIT(24), 674 .clk_mask = BIT(11) | BIT(12) | BIT(24), 675 }, 676 [IMX8MP_MEDIABLK_PD_ISP] = { 677 .name = "mediablk-isp", 678 .clk_names = (const char *[]){ "isp", "axi", "apb" }, 679 .num_clks = 3, 680 .gpc_name = "isp", 681 .rst_mask = BIT(16) | BIT(17) | BIT(18), 682 .clk_mask = BIT(16) | BIT(17) | BIT(18), 683 }, 684 [IMX8MP_MEDIABLK_PD_DWE] = { 685 .name = "mediablk-dwe", 686 .clk_names = (const char *[]){ "axi", "apb" }, 687 .num_clks = 2, 688 .gpc_name = "dwe", 689 .rst_mask = BIT(19) | BIT(20) | BIT(21), 690 .clk_mask = BIT(19) | BIT(20) | BIT(21), 691 }, 692 [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = { 693 .name = "mediablk-mipi-dsi-2", 694 .clk_names = (const char *[]){ "phy", }, 695 .num_clks = 1, 696 .gpc_name = "mipi-dsi2", 697 .rst_mask = BIT(22), 698 .clk_mask = BIT(22), 699 .mipi_phy_rst_mask = BIT(29), 700 }, 701}; 702 703static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = { 704 .max_reg = 0x138, 705 .power_notifier_fn = imx8mp_media_power_notifier, 706 .domains = imx8mp_media_blk_ctl_domain_data, 707 .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data), 708}; 709 710static int imx8mq_vpu_power_notifier(struct notifier_block *nb, 711 unsigned long action, void *data) 712{ 713 struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, 714 power_nb); 715 716 if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) 717 return NOTIFY_OK; 718 719 /* 720 * The ADB in the VPUMIX domain has no separate reset and clock 721 * enable bits, but is ungated and reset together with the VPUs. The 722 * reset and clock enable inputs to the ADB is a logical OR of the 723 * VPU bits. In order to set the G2 fuse bits, the G2 clock must 724 * also be enabled. 725 */ 726 regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1)); 727 regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1)); 728 729 if (action == GENPD_NOTIFY_ON) { 730 /* 731 * On power up we have no software backchannel to the GPC to 732 * wait for the ADB handshake to happen, so we just delay for a 733 * bit. On power down the GPC driver waits for the handshake. 734 */ 735 udelay(5); 736 737 /* set "fuse" bits to enable the VPUs */ 738 regmap_set_bits(bc->regmap, 0x8, 0xffffffff); 739 regmap_set_bits(bc->regmap, 0xc, 0xffffffff); 740 regmap_set_bits(bc->regmap, 0x10, 0xffffffff); 741 } 742 743 return NOTIFY_OK; 744} 745 746static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = { 747 [IMX8MQ_VPUBLK_PD_G1] = { 748 .name = "vpublk-g1", 749 .clk_names = (const char *[]){ "g1", }, 750 .num_clks = 1, 751 .gpc_name = "g1", 752 .rst_mask = BIT(1), 753 .clk_mask = BIT(1), 754 }, 755 [IMX8MQ_VPUBLK_PD_G2] = { 756 .name = "vpublk-g2", 757 .clk_names = (const char *[]){ "g2", }, 758 .num_clks = 1, 759 .gpc_name = "g2", 760 .rst_mask = BIT(0), 761 .clk_mask = BIT(0), 762 }, 763}; 764 765static const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = { 766 .max_reg = 0x14, 767 .power_notifier_fn = imx8mq_vpu_power_notifier, 768 .domains = imx8mq_vpu_blk_ctl_domain_data, 769 .num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data), 770}; 771 772static const struct of_device_id imx8m_blk_ctrl_of_match[] = { 773 { 774 .compatible = "fsl,imx8mm-vpu-blk-ctrl", 775 .data = &imx8mm_vpu_blk_ctl_dev_data 776 }, { 777 .compatible = "fsl,imx8mm-disp-blk-ctrl", 778 .data = &imx8mm_disp_blk_ctl_dev_data 779 }, { 780 .compatible = "fsl,imx8mn-disp-blk-ctrl", 781 .data = &imx8mn_disp_blk_ctl_dev_data 782 }, { 783 .compatible = "fsl,imx8mp-media-blk-ctrl", 784 .data = &imx8mp_media_blk_ctl_dev_data 785 }, { 786 .compatible = "fsl,imx8mq-vpu-blk-ctrl", 787 .data = &imx8mq_vpu_blk_ctl_dev_data 788 }, { 789 /* Sentinel */ 790 } 791}; 792MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match); 793 794static struct platform_driver imx8m_blk_ctrl_driver = { 795 .probe = imx8m_blk_ctrl_probe, 796 .remove = imx8m_blk_ctrl_remove, 797 .driver = { 798 .name = "imx8m-blk-ctrl", 799 .pm = &imx8m_blk_ctrl_pm_ops, 800 .of_match_table = imx8m_blk_ctrl_of_match, 801 }, 802}; 803module_platform_driver(imx8m_blk_ctrl_driver);