FILE
bcm2835-power.c
File preview with syntax highlighting.
linux-6.19.14/drivers/pmdomain/bcm/bcm2835-power.c
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Power domain driver for Broadcom BCM2835 4 * 5 * Copyright (C) 2018 Broadcom 6 */ 7 8#include <dt-bindings/soc/bcm2835-pm.h> 9#include <linux/clk.h> 10#include <linux/delay.h> 11#include <linux/io.h> 12#include <linux/iopoll.h> 13#include <linux/mfd/bcm2835-pm.h> 14#include <linux/module.h> 15#include <linux/platform_device.h> 16#include <linux/pm_domain.h> 17#include <linux/reset-controller.h> 18#include <linux/types.h> 19 20#define PM_GNRIC 0x00 21#define PM_AUDIO 0x04 22#define PM_STATUS 0x18 23#define PM_RSTC 0x1c 24#define PM_RSTS 0x20 25#define PM_WDOG 0x24 26#define PM_PADS0 0x28 27#define PM_PADS2 0x2c 28#define PM_PADS3 0x30 29#define PM_PADS4 0x34 30#define PM_PADS5 0x38 31#define PM_PADS6 0x3c 32#define PM_CAM0 0x44 33#define PM_CAM0_LDOHPEN BIT(2) 34#define PM_CAM0_LDOLPEN BIT(1) 35#define PM_CAM0_CTRLEN BIT(0) 36 37#define PM_CAM1 0x48 38#define PM_CAM1_LDOHPEN BIT(2) 39#define PM_CAM1_LDOLPEN BIT(1) 40#define PM_CAM1_CTRLEN BIT(0) 41 42#define PM_CCP2TX 0x4c 43#define PM_CCP2TX_LDOEN BIT(1) 44#define PM_CCP2TX_CTRLEN BIT(0) 45 46#define PM_DSI0 0x50 47#define PM_DSI0_LDOHPEN BIT(2) 48#define PM_DSI0_LDOLPEN BIT(1) 49#define PM_DSI0_CTRLEN BIT(0) 50 51#define PM_DSI1 0x54 52#define PM_DSI1_LDOHPEN BIT(2) 53#define PM_DSI1_LDOLPEN BIT(1) 54#define PM_DSI1_CTRLEN BIT(0) 55 56#define PM_HDMI 0x58 57#define PM_HDMI_RSTDR BIT(19) 58#define PM_HDMI_LDOPD BIT(1) 59#define PM_HDMI_CTRLEN BIT(0) 60 61#define PM_USB 0x5c 62/* The power gates must be enabled with this bit before enabling the LDO in the 63 * USB block. 64 */ 65#define PM_USB_CTRLEN BIT(0) 66 67#define PM_PXLDO 0x60 68#define PM_PXBG 0x64 69#define PM_DFT 0x68 70#define PM_SMPS 0x6c 71#define PM_XOSC 0x70 72#define PM_SPAREW 0x74 73#define PM_SPARER 0x78 74#define PM_AVS_RSTDR 0x7c 75#define PM_AVS_STAT 0x80 76#define PM_AVS_EVENT 0x84 77#define PM_AVS_INTEN 0x88 78#define PM_DUMMY 0xfc 79 80#define PM_IMAGE 0x108 81#define PM_GRAFX 0x10c 82#define PM_PROC 0x110 83#define PM_GRAFX_2712 0x304 84#define PM_ENAB BIT(12) 85#define PM_ISPRSTN BIT(8) 86#define PM_H264RSTN BIT(7) 87#define PM_PERIRSTN BIT(6) 88#define PM_V3DRSTN BIT(6) 89#define PM_ISFUNC BIT(5) 90#define PM_MRDONE BIT(4) 91#define PM_MEMREP BIT(3) 92#define PM_ISPOW BIT(2) 93#define PM_POWOK BIT(1) 94#define PM_POWUP BIT(0) 95#define PM_INRUSH_SHIFT 13 96#define PM_INRUSH_3_5_MA 0 97#define PM_INRUSH_5_MA 1 98#define PM_INRUSH_10_MA 2 99#define PM_INRUSH_20_MA 3 100#define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT) 101 102#define PM_PASSWORD 0x5a000000 103 104#define PM_WDOG_TIME_SET 0x000fffff 105#define PM_RSTC_WRCFG_CLR 0xffffffcf 106#define PM_RSTS_HADWRH_SET 0x00000040 107#define PM_RSTC_WRCFG_SET 0x00000030 108#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 109#define PM_RSTC_RESET 0x00000102 110 111#define PM_READ(reg) readl(power->base + (reg)) 112#define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg)) 113 114#define ASB_BRDG_VERSION 0x00 115#define ASB_CPR_CTRL 0x04 116 117#define ASB_V3D_S_CTRL 0x08 118#define ASB_V3D_M_CTRL 0x0c 119#define ASB_ISP_S_CTRL 0x10 120#define ASB_ISP_M_CTRL 0x14 121#define ASB_H264_S_CTRL 0x18 122#define ASB_H264_M_CTRL 0x1c 123 124#define ASB_REQ_STOP BIT(0) 125#define ASB_ACK BIT(1) 126#define ASB_EMPTY BIT(2) 127#define ASB_FULL BIT(3) 128 129#define ASB_AXI_BRDG_ID 0x20 130 131#define BCM2835_BRDG_ID 0x62726467 132 133struct bcm2835_power_domain { 134 struct generic_pm_domain base; 135 struct bcm2835_power *power; 136 u32 domain; 137 struct clk *clk; 138}; 139 140struct bcm2835_power { 141 struct device *dev; 142 /* PM registers. */ 143 void __iomem *base; 144 /* AXI Async bridge registers. */ 145 void __iomem *asb; 146 /* RPiVid bridge registers. */ 147 void __iomem *rpivid_asb; 148 149 struct genpd_onecell_data pd_xlate; 150 struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; 151 struct reset_controller_dev reset; 152}; 153 154static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable) 155{ 156 void __iomem *base = power->asb; 157 u32 val; 158 159 switch (reg) { 160 case 0: 161 return 0; 162 case ASB_V3D_S_CTRL: 163 case ASB_V3D_M_CTRL: 164 if (power->rpivid_asb) 165 base = power->rpivid_asb; 166 break; 167 } 168 169 /* Enable the module's async AXI bridges. */ 170 if (enable) { 171 val = readl(base + reg) & ~ASB_REQ_STOP; 172 } else { 173 val = readl(base + reg) | ASB_REQ_STOP; 174 } 175 writel(PM_PASSWORD | val, base + reg); 176 177 if (readl_poll_timeout_atomic(base + reg, val, 178 !!(val & ASB_ACK) != enable, 0, 5)) 179 return -ETIMEDOUT; 180 181 return 0; 182} 183 184static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) 185{ 186 return bcm2835_asb_control(power, reg, true); 187} 188 189static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) 190{ 191 return bcm2835_asb_control(power, reg, false); 192} 193 194static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) 195{ 196 struct bcm2835_power *power = pd->power; 197 198 /* We don't run this on BCM2711 */ 199 if (power->rpivid_asb) 200 return 0; 201 202 /* Enable functional isolation */ 203 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); 204 205 /* Enable electrical isolation */ 206 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); 207 208 /* Open the power switches. */ 209 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP); 210 211 return 0; 212} 213 214static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) 215{ 216 struct bcm2835_power *power = pd->power; 217 struct device *dev = power->dev; 218 u64 start; 219 int ret; 220 int inrush; 221 bool powok; 222 223 /* We don't run this on BCM2711 */ 224 if (power->rpivid_asb) 225 return 0; 226 227 /* If it was already powered on by the fw, leave it that way. */ 228 if (PM_READ(pm_reg) & PM_POWUP) 229 return 0; 230 231 /* Enable power. Allowing too much current at once may result 232 * in POWOK never getting set, so start low and ramp it up as 233 * necessary to succeed. 234 */ 235 powok = false; 236 for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) { 237 PM_WRITE(pm_reg, 238 (PM_READ(pm_reg) & ~PM_INRUSH_MASK) | 239 (inrush << PM_INRUSH_SHIFT) | 240 PM_POWUP); 241 242 start = ktime_get_ns(); 243 while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) { 244 cpu_relax(); 245 if (ktime_get_ns() - start >= 3000) 246 break; 247 } 248 } 249 if (!powok) { 250 dev_err(dev, "Timeout waiting for %s power OK\n", 251 pd->base.name); 252 ret = -ETIMEDOUT; 253 goto err_disable_powup; 254 } 255 256 /* Disable electrical isolation */ 257 PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW); 258 259 /* Repair memory */ 260 PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP); 261 start = ktime_get_ns(); 262 while (!(PM_READ(pm_reg) & PM_MRDONE)) { 263 cpu_relax(); 264 if (ktime_get_ns() - start >= 1000) { 265 dev_err(dev, "Timeout waiting for %s memory repair\n", 266 pd->base.name); 267 ret = -ETIMEDOUT; 268 goto err_disable_ispow; 269 } 270 } 271 272 /* Disable functional isolation */ 273 PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC); 274 275 return 0; 276 277err_disable_ispow: 278 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); 279err_disable_powup: 280 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK)); 281 return ret; 282} 283 284static int bcm2835_asb_power_on(struct bcm2835_power_domain *pd, 285 u32 pm_reg, 286 u32 asb_m_reg, 287 u32 asb_s_reg, 288 u32 reset_flags) 289{ 290 struct bcm2835_power *power = pd->power; 291 int ret; 292 293 ret = clk_prepare_enable(pd->clk); 294 if (ret) { 295 dev_err(power->dev, "Failed to enable clock for %s\n", 296 pd->base.name); 297 return ret; 298 } 299 300 /* Wait 32 clocks for reset to propagate, 1 us will be enough */ 301 udelay(1); 302 303 clk_disable_unprepare(pd->clk); 304 305 /* Deassert the resets. */ 306 PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags); 307 308 ret = clk_prepare_enable(pd->clk); 309 if (ret) { 310 dev_err(power->dev, "Failed to enable clock for %s\n", 311 pd->base.name); 312 goto err_enable_resets; 313 } 314 315 ret = bcm2835_asb_enable(power, asb_m_reg); 316 if (ret) { 317 dev_err(power->dev, "Failed to enable ASB master for %s\n", 318 pd->base.name); 319 goto err_disable_clk; 320 } 321 ret = bcm2835_asb_enable(power, asb_s_reg); 322 if (ret) { 323 dev_err(power->dev, "Failed to enable ASB slave for %s\n", 324 pd->base.name); 325 goto err_disable_asb_master; 326 } 327 328 return 0; 329 330err_disable_asb_master: 331 bcm2835_asb_disable(power, asb_m_reg); 332err_disable_clk: 333 clk_disable_unprepare(pd->clk); 334err_enable_resets: 335 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); 336 return ret; 337} 338 339static int bcm2835_asb_power_off(struct bcm2835_power_domain *pd, 340 u32 pm_reg, 341 u32 asb_m_reg, 342 u32 asb_s_reg, 343 u32 reset_flags) 344{ 345 struct bcm2835_power *power = pd->power; 346 int ret; 347 348 ret = bcm2835_asb_disable(power, asb_s_reg); 349 if (ret) { 350 dev_warn(power->dev, "Failed to disable ASB slave for %s\n", 351 pd->base.name); 352 return ret; 353 } 354 ret = bcm2835_asb_disable(power, asb_m_reg); 355 if (ret) { 356 dev_warn(power->dev, "Failed to disable ASB master for %s\n", 357 pd->base.name); 358 bcm2835_asb_enable(power, asb_s_reg); 359 return ret; 360 } 361 362 clk_disable_unprepare(pd->clk); 363 364 /* Assert the resets. */ 365 PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); 366 367 return 0; 368} 369 370static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain) 371{ 372 struct bcm2835_power_domain *pd = 373 container_of(domain, struct bcm2835_power_domain, base); 374 struct bcm2835_power *power = pd->power; 375 376 switch (pd->domain) { 377 case BCM2835_POWER_DOMAIN_GRAFX: 378 return bcm2835_power_power_on(pd, PM_GRAFX); 379 380 case BCM2835_POWER_DOMAIN_GRAFX_V3D: 381 if (!power->asb) 382 return bcm2835_asb_power_on(pd, PM_GRAFX_2712, 383 0, 0, PM_V3DRSTN); 384 return bcm2835_asb_power_on(pd, PM_GRAFX, 385 ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, 386 PM_V3DRSTN); 387 388 case BCM2835_POWER_DOMAIN_IMAGE: 389 return bcm2835_power_power_on(pd, PM_IMAGE); 390 391 case BCM2835_POWER_DOMAIN_IMAGE_PERI: 392 return bcm2835_asb_power_on(pd, PM_IMAGE, 393 0, 0, 394 PM_PERIRSTN); 395 396 case BCM2835_POWER_DOMAIN_IMAGE_ISP: 397 return bcm2835_asb_power_on(pd, PM_IMAGE, 398 ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, 399 PM_ISPRSTN); 400 401 case BCM2835_POWER_DOMAIN_IMAGE_H264: 402 return bcm2835_asb_power_on(pd, PM_IMAGE, 403 ASB_H264_M_CTRL, ASB_H264_S_CTRL, 404 PM_H264RSTN); 405 406 case BCM2835_POWER_DOMAIN_USB: 407 PM_WRITE(PM_USB, PM_USB_CTRLEN); 408 return 0; 409 410 case BCM2835_POWER_DOMAIN_DSI0: 411 PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); 412 PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN); 413 return 0; 414 415 case BCM2835_POWER_DOMAIN_DSI1: 416 PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); 417 PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN); 418 return 0; 419 420 case BCM2835_POWER_DOMAIN_CCP2TX: 421 PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); 422 PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN); 423 return 0; 424 425 case BCM2835_POWER_DOMAIN_HDMI: 426 PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR); 427 PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN); 428 PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD); 429 usleep_range(100, 200); 430 PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR); 431 return 0; 432 433 default: 434 dev_err(power->dev, "Invalid domain %d\n", pd->domain); 435 return -EINVAL; 436 } 437} 438 439static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain) 440{ 441 struct bcm2835_power_domain *pd = 442 container_of(domain, struct bcm2835_power_domain, base); 443 struct bcm2835_power *power = pd->power; 444 445 switch (pd->domain) { 446 case BCM2835_POWER_DOMAIN_GRAFX: 447 return bcm2835_power_power_off(pd, PM_GRAFX); 448 449 case BCM2835_POWER_DOMAIN_GRAFX_V3D: 450 if (!power->asb) 451 return bcm2835_asb_power_off(pd, PM_GRAFX_2712, 452 0, 0, PM_V3DRSTN); 453 return bcm2835_asb_power_off(pd, PM_GRAFX, 454 ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, 455 PM_V3DRSTN); 456 457 case BCM2835_POWER_DOMAIN_IMAGE: 458 return bcm2835_power_power_off(pd, PM_IMAGE); 459 460 case BCM2835_POWER_DOMAIN_IMAGE_PERI: 461 return bcm2835_asb_power_off(pd, PM_IMAGE, 462 0, 0, 463 PM_PERIRSTN); 464 465 case BCM2835_POWER_DOMAIN_IMAGE_ISP: 466 return bcm2835_asb_power_off(pd, PM_IMAGE, 467 ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, 468 PM_ISPRSTN); 469 470 case BCM2835_POWER_DOMAIN_IMAGE_H264: 471 return bcm2835_asb_power_off(pd, PM_IMAGE, 472 ASB_H264_M_CTRL, ASB_H264_S_CTRL, 473 PM_H264RSTN); 474 475 case BCM2835_POWER_DOMAIN_USB: 476 PM_WRITE(PM_USB, 0); 477 return 0; 478 479 case BCM2835_POWER_DOMAIN_DSI0: 480 PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); 481 PM_WRITE(PM_DSI0, 0); 482 return 0; 483 484 case BCM2835_POWER_DOMAIN_DSI1: 485 PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); 486 PM_WRITE(PM_DSI1, 0); 487 return 0; 488 489 case BCM2835_POWER_DOMAIN_CCP2TX: 490 PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); 491 PM_WRITE(PM_CCP2TX, 0); 492 return 0; 493 494 case BCM2835_POWER_DOMAIN_HDMI: 495 PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD); 496 PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN); 497 return 0; 498 499 default: 500 dev_err(power->dev, "Invalid domain %d\n", pd->domain); 501 return -EINVAL; 502 } 503} 504 505static int 506bcm2835_init_power_domain(struct bcm2835_power *power, 507 int pd_xlate_index, const char *name) 508{ 509 struct device *dev = power->dev; 510 struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index]; 511 512 dom->clk = devm_clk_get_optional(dev->parent, name); 513 if (IS_ERR(dom->clk)) 514 return dev_err_probe(dev, PTR_ERR(dom->clk), "Failed to get clock %s\n", 515 name); 516 517 dom->base.name = name; 518 dom->base.flags = GENPD_FLAG_ACTIVE_WAKEUP; 519 dom->base.power_on = bcm2835_power_pd_power_on; 520 dom->base.power_off = bcm2835_power_pd_power_off; 521 522 dom->domain = pd_xlate_index; 523 dom->power = power; 524 525 /* XXX: on/off at boot? */ 526 pm_genpd_init(&dom->base, NULL, true); 527 528 power->pd_xlate.domains[pd_xlate_index] = &dom->base; 529 530 return 0; 531} 532 533/** bcm2835_reset_reset - Resets a block that has a reset line in the 534 * PM block. 535 * 536 * The consumer of the reset controller must have the power domain up 537 * -- there's no reset ability with the power domain down. To reset 538 * the sub-block, we just disable its access to memory through the 539 * ASB, reset, and re-enable. 540 */ 541static int bcm2835_reset_reset(struct reset_controller_dev *rcdev, 542 unsigned long id) 543{ 544 struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, 545 reset); 546 struct bcm2835_power_domain *pd; 547 int ret; 548 549 switch (id) { 550 case BCM2835_RESET_V3D: 551 pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D]; 552 break; 553 case BCM2835_RESET_H264: 554 pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264]; 555 break; 556 case BCM2835_RESET_ISP: 557 pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP]; 558 break; 559 default: 560 dev_err(power->dev, "Bad reset id %ld\n", id); 561 return -EINVAL; 562 } 563 564 ret = bcm2835_power_pd_power_off(&pd->base); 565 if (ret) 566 return ret; 567 568 return bcm2835_power_pd_power_on(&pd->base); 569} 570 571static int bcm2835_reset_status(struct reset_controller_dev *rcdev, 572 unsigned long id) 573{ 574 struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, 575 reset); 576 577 switch (id) { 578 case BCM2835_RESET_V3D: 579 return !(PM_READ(PM_GRAFX) & PM_V3DRSTN); 580 case BCM2835_RESET_H264: 581 return !(PM_READ(PM_IMAGE) & PM_H264RSTN); 582 case BCM2835_RESET_ISP: 583 return !(PM_READ(PM_IMAGE) & PM_ISPRSTN); 584 default: 585 return -EINVAL; 586 } 587} 588 589static const struct reset_control_ops bcm2835_reset_ops = { 590 .reset = bcm2835_reset_reset, 591 .status = bcm2835_reset_status, 592}; 593 594static const char *const power_domain_names[] = { 595 [BCM2835_POWER_DOMAIN_GRAFX] = "grafx", 596 [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d", 597 598 [BCM2835_POWER_DOMAIN_IMAGE] = "image", 599 [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image", 600 [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264", 601 [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp", 602 603 [BCM2835_POWER_DOMAIN_USB] = "usb", 604 [BCM2835_POWER_DOMAIN_DSI0] = "dsi0", 605 [BCM2835_POWER_DOMAIN_DSI1] = "dsi1", 606 [BCM2835_POWER_DOMAIN_CAM0] = "cam0", 607 [BCM2835_POWER_DOMAIN_CAM1] = "cam1", 608 [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx", 609 [BCM2835_POWER_DOMAIN_HDMI] = "hdmi", 610}; 611 612static int bcm2835_power_probe(struct platform_device *pdev) 613{ 614 struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); 615 struct device *dev = &pdev->dev; 616 struct bcm2835_power *power; 617 static const struct { 618 int parent, child; 619 } domain_deps[] = { 620 { BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D }, 621 { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI }, 622 { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 }, 623 { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP }, 624 { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB }, 625 { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 }, 626 { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 }, 627 }; 628 int ret = 0, i; 629 u32 id; 630 631 power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); 632 if (!power) 633 return -ENOMEM; 634 platform_set_drvdata(pdev, power); 635 636 power->dev = dev; 637 power->base = pm->base; 638 power->asb = pm->asb; 639 power->rpivid_asb = pm->rpivid_asb; 640 641 if (power->asb) { 642 id = readl(power->asb + ASB_AXI_BRDG_ID); 643 if (id != BCM2835_BRDG_ID /* "BRDG" */) { 644 dev_err(dev, "ASB register ID returned 0x%08x\n", id); 645 return -ENODEV; 646 } 647 } 648 649 if (power->rpivid_asb) { 650 id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID); 651 if (id != BCM2835_BRDG_ID /* "BRDG" */) { 652 dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n", 653 id); 654 return -ENODEV; 655 } 656 } 657 658 power->pd_xlate.domains = devm_kcalloc(dev, 659 ARRAY_SIZE(power_domain_names), 660 sizeof(*power->pd_xlate.domains), 661 GFP_KERNEL); 662 if (!power->pd_xlate.domains) 663 return -ENOMEM; 664 665 power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names); 666 667 for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { 668 ret = bcm2835_init_power_domain(power, i, power_domain_names[i]); 669 if (ret) 670 goto fail; 671 } 672 673 for (i = 0; i < ARRAY_SIZE(domain_deps); i++) { 674 pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base, 675 &power->domains[domain_deps[i].child].base); 676 } 677 678 power->reset.owner = THIS_MODULE; 679 power->reset.nr_resets = BCM2835_RESET_COUNT; 680 power->reset.ops = &bcm2835_reset_ops; 681 power->reset.of_node = dev->parent->of_node; 682 683 ret = devm_reset_controller_register(dev, &power->reset); 684 if (ret) 685 goto fail; 686 687 of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate); 688 689 dev_info(dev, "Broadcom BCM2835 power domains driver"); 690 return 0; 691 692fail: 693 for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { 694 struct generic_pm_domain *dom = &power->domains[i].base; 695 696 if (dom->name) 697 pm_genpd_remove(dom); 698 } 699 return ret; 700} 701 702static struct platform_driver bcm2835_power_driver = { 703 .probe = bcm2835_power_probe, 704 .driver = { 705 .name = "bcm2835-power", 706 }, 707}; 708module_platform_driver(bcm2835_power_driver); 709 710MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); 711MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset");
~/
Read-only file preview.