Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Power domain driver for Broadcom BCM2835
0004  *
0005  * Copyright (C) 2018 Broadcom
0006  */
0007 
0008 #include <dt-bindings/soc/bcm2835-pm.h>
0009 #include <linux/clk.h>
0010 #include <linux/delay.h>
0011 #include <linux/io.h>
0012 #include <linux/mfd/bcm2835-pm.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_domain.h>
0016 #include <linux/reset-controller.h>
0017 #include <linux/types.h>
0018 
0019 #define PM_GNRIC                        0x00
0020 #define PM_AUDIO                        0x04
0021 #define PM_STATUS                       0x18
0022 #define PM_RSTC             0x1c
0023 #define PM_RSTS             0x20
0024 #define PM_WDOG             0x24
0025 #define PM_PADS0            0x28
0026 #define PM_PADS2            0x2c
0027 #define PM_PADS3            0x30
0028 #define PM_PADS4            0x34
0029 #define PM_PADS5            0x38
0030 #define PM_PADS6            0x3c
0031 #define PM_CAM0             0x44
0032 #define PM_CAM0_LDOHPEN         BIT(2)
0033 #define PM_CAM0_LDOLPEN         BIT(1)
0034 #define PM_CAM0_CTRLEN          BIT(0)
0035 
0036 #define PM_CAM1             0x48
0037 #define PM_CAM1_LDOHPEN         BIT(2)
0038 #define PM_CAM1_LDOLPEN         BIT(1)
0039 #define PM_CAM1_CTRLEN          BIT(0)
0040 
0041 #define PM_CCP2TX           0x4c
0042 #define PM_CCP2TX_LDOEN         BIT(1)
0043 #define PM_CCP2TX_CTRLEN        BIT(0)
0044 
0045 #define PM_DSI0             0x50
0046 #define PM_DSI0_LDOHPEN         BIT(2)
0047 #define PM_DSI0_LDOLPEN         BIT(1)
0048 #define PM_DSI0_CTRLEN          BIT(0)
0049 
0050 #define PM_DSI1             0x54
0051 #define PM_DSI1_LDOHPEN         BIT(2)
0052 #define PM_DSI1_LDOLPEN         BIT(1)
0053 #define PM_DSI1_CTRLEN          BIT(0)
0054 
0055 #define PM_HDMI             0x58
0056 #define PM_HDMI_RSTDR           BIT(19)
0057 #define PM_HDMI_LDOPD           BIT(1)
0058 #define PM_HDMI_CTRLEN          BIT(0)
0059 
0060 #define PM_USB              0x5c
0061 /* The power gates must be enabled with this bit before enabling the LDO in the
0062  * USB block.
0063  */
0064 #define PM_USB_CTRLEN           BIT(0)
0065 
0066 #define PM_PXLDO            0x60
0067 #define PM_PXBG             0x64
0068 #define PM_DFT              0x68
0069 #define PM_SMPS             0x6c
0070 #define PM_XOSC             0x70
0071 #define PM_SPAREW           0x74
0072 #define PM_SPARER           0x78
0073 #define PM_AVS_RSTDR            0x7c
0074 #define PM_AVS_STAT         0x80
0075 #define PM_AVS_EVENT            0x84
0076 #define PM_AVS_INTEN            0x88
0077 #define PM_DUMMY            0xfc
0078 
0079 #define PM_IMAGE            0x108
0080 #define PM_GRAFX            0x10c
0081 #define PM_PROC             0x110
0082 #define PM_ENAB             BIT(12)
0083 #define PM_ISPRSTN          BIT(8)
0084 #define PM_H264RSTN         BIT(7)
0085 #define PM_PERIRSTN         BIT(6)
0086 #define PM_V3DRSTN          BIT(6)
0087 #define PM_ISFUNC           BIT(5)
0088 #define PM_MRDONE           BIT(4)
0089 #define PM_MEMREP           BIT(3)
0090 #define PM_ISPOW            BIT(2)
0091 #define PM_POWOK            BIT(1)
0092 #define PM_POWUP            BIT(0)
0093 #define PM_INRUSH_SHIFT         13
0094 #define PM_INRUSH_3_5_MA        0
0095 #define PM_INRUSH_5_MA          1
0096 #define PM_INRUSH_10_MA         2
0097 #define PM_INRUSH_20_MA         3
0098 #define PM_INRUSH_MASK          (3 << PM_INRUSH_SHIFT)
0099 
0100 #define PM_PASSWORD         0x5a000000
0101 
0102 #define PM_WDOG_TIME_SET        0x000fffff
0103 #define PM_RSTC_WRCFG_CLR       0xffffffcf
0104 #define PM_RSTS_HADWRH_SET      0x00000040
0105 #define PM_RSTC_WRCFG_SET       0x00000030
0106 #define PM_RSTC_WRCFG_FULL_RESET    0x00000020
0107 #define PM_RSTC_RESET           0x00000102
0108 
0109 #define PM_READ(reg) readl(power->base + (reg))
0110 #define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg))
0111 
0112 #define ASB_BRDG_VERSION                0x00
0113 #define ASB_CPR_CTRL                    0x04
0114 
0115 #define ASB_V3D_S_CTRL          0x08
0116 #define ASB_V3D_M_CTRL          0x0c
0117 #define ASB_ISP_S_CTRL          0x10
0118 #define ASB_ISP_M_CTRL          0x14
0119 #define ASB_H264_S_CTRL         0x18
0120 #define ASB_H264_M_CTRL         0x1c
0121 
0122 #define ASB_REQ_STOP                    BIT(0)
0123 #define ASB_ACK                         BIT(1)
0124 #define ASB_EMPTY                       BIT(2)
0125 #define ASB_FULL                        BIT(3)
0126 
0127 #define ASB_AXI_BRDG_ID         0x20
0128 
0129 #define BCM2835_BRDG_ID         0x62726467
0130 
0131 struct bcm2835_power_domain {
0132     struct generic_pm_domain base;
0133     struct bcm2835_power *power;
0134     u32 domain;
0135     struct clk *clk;
0136 };
0137 
0138 struct bcm2835_power {
0139     struct device       *dev;
0140     /* PM registers. */
0141     void __iomem        *base;
0142     /* AXI Async bridge registers. */
0143     void __iomem        *asb;
0144     /* RPiVid bridge registers. */
0145     void __iomem        *rpivid_asb;
0146 
0147     struct genpd_onecell_data pd_xlate;
0148     struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
0149     struct reset_controller_dev reset;
0150 };
0151 
0152 static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable)
0153 {
0154     void __iomem *base = power->asb;
0155     u64 start;
0156     u32 val;
0157 
0158     switch (reg) {
0159     case 0:
0160         return 0;
0161     case ASB_V3D_S_CTRL:
0162     case ASB_V3D_M_CTRL:
0163         if (power->rpivid_asb)
0164             base = power->rpivid_asb;
0165         break;
0166     }
0167 
0168     start = ktime_get_ns();
0169 
0170     /* Enable the module's async AXI bridges. */
0171     if (enable) {
0172         val = readl(base + reg) & ~ASB_REQ_STOP;
0173     } else {
0174         val = readl(base + reg) | ASB_REQ_STOP;
0175     }
0176     writel(PM_PASSWORD | val, base + reg);
0177 
0178     while (readl(base + reg) & ASB_ACK) {
0179         cpu_relax();
0180         if (ktime_get_ns() - start >= 1000)
0181             return -ETIMEDOUT;
0182     }
0183 
0184     return 0;
0185 }
0186 
0187 static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
0188 {
0189     return bcm2835_asb_control(power, reg, true);
0190 }
0191 
0192 static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg)
0193 {
0194     return bcm2835_asb_control(power, reg, false);
0195 }
0196 
0197 static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg)
0198 {
0199     struct bcm2835_power *power = pd->power;
0200 
0201     /* We don't run this on BCM2711 */
0202     if (power->rpivid_asb)
0203         return 0;
0204 
0205     /* Enable functional isolation */
0206     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC);
0207 
0208     /* Enable electrical isolation */
0209     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW);
0210 
0211     /* Open the power switches. */
0212     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP);
0213 
0214     return 0;
0215 }
0216 
0217 static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg)
0218 {
0219     struct bcm2835_power *power = pd->power;
0220     struct device *dev = power->dev;
0221     u64 start;
0222     int ret;
0223     int inrush;
0224     bool powok;
0225 
0226     /* We don't run this on BCM2711 */
0227     if (power->rpivid_asb)
0228         return 0;
0229 
0230     /* If it was already powered on by the fw, leave it that way. */
0231     if (PM_READ(pm_reg) & PM_POWUP)
0232         return 0;
0233 
0234     /* Enable power.  Allowing too much current at once may result
0235      * in POWOK never getting set, so start low and ramp it up as
0236      * necessary to succeed.
0237      */
0238     powok = false;
0239     for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) {
0240         PM_WRITE(pm_reg,
0241              (PM_READ(pm_reg) & ~PM_INRUSH_MASK) |
0242              (inrush << PM_INRUSH_SHIFT) |
0243              PM_POWUP);
0244 
0245         start = ktime_get_ns();
0246         while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) {
0247             cpu_relax();
0248             if (ktime_get_ns() - start >= 3000)
0249                 break;
0250         }
0251     }
0252     if (!powok) {
0253         dev_err(dev, "Timeout waiting for %s power OK\n",
0254             pd->base.name);
0255         ret = -ETIMEDOUT;
0256         goto err_disable_powup;
0257     }
0258 
0259     /* Disable electrical isolation */
0260     PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW);
0261 
0262     /* Repair memory */
0263     PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP);
0264     start = ktime_get_ns();
0265     while (!(PM_READ(pm_reg) & PM_MRDONE)) {
0266         cpu_relax();
0267         if (ktime_get_ns() - start >= 1000) {
0268             dev_err(dev, "Timeout waiting for %s memory repair\n",
0269                 pd->base.name);
0270             ret = -ETIMEDOUT;
0271             goto err_disable_ispow;
0272         }
0273     }
0274 
0275     /* Disable functional isolation */
0276     PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC);
0277 
0278     return 0;
0279 
0280 err_disable_ispow:
0281     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW);
0282 err_disable_powup:
0283     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK));
0284     return ret;
0285 }
0286 
0287 static int bcm2835_asb_power_on(struct bcm2835_power_domain *pd,
0288                 u32 pm_reg,
0289                 u32 asb_m_reg,
0290                 u32 asb_s_reg,
0291                 u32 reset_flags)
0292 {
0293     struct bcm2835_power *power = pd->power;
0294     int ret;
0295 
0296     ret = clk_prepare_enable(pd->clk);
0297     if (ret) {
0298         dev_err(power->dev, "Failed to enable clock for %s\n",
0299             pd->base.name);
0300         return ret;
0301     }
0302 
0303     /* Wait 32 clocks for reset to propagate, 1 us will be enough */
0304     udelay(1);
0305 
0306     clk_disable_unprepare(pd->clk);
0307 
0308     /* Deassert the resets. */
0309     PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags);
0310 
0311     ret = clk_prepare_enable(pd->clk);
0312     if (ret) {
0313         dev_err(power->dev, "Failed to enable clock for %s\n",
0314             pd->base.name);
0315         goto err_enable_resets;
0316     }
0317 
0318     ret = bcm2835_asb_enable(power, asb_m_reg);
0319     if (ret) {
0320         dev_err(power->dev, "Failed to enable ASB master for %s\n",
0321             pd->base.name);
0322         goto err_disable_clk;
0323     }
0324     ret = bcm2835_asb_enable(power, asb_s_reg);
0325     if (ret) {
0326         dev_err(power->dev, "Failed to enable ASB slave for %s\n",
0327             pd->base.name);
0328         goto err_disable_asb_master;
0329     }
0330 
0331     return 0;
0332 
0333 err_disable_asb_master:
0334     bcm2835_asb_disable(power, asb_m_reg);
0335 err_disable_clk:
0336     clk_disable_unprepare(pd->clk);
0337 err_enable_resets:
0338     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags);
0339     return ret;
0340 }
0341 
0342 static int bcm2835_asb_power_off(struct bcm2835_power_domain *pd,
0343                  u32 pm_reg,
0344                  u32 asb_m_reg,
0345                  u32 asb_s_reg,
0346                  u32 reset_flags)
0347 {
0348     struct bcm2835_power *power = pd->power;
0349     int ret;
0350 
0351     ret = bcm2835_asb_disable(power, asb_s_reg);
0352     if (ret) {
0353         dev_warn(power->dev, "Failed to disable ASB slave for %s\n",
0354              pd->base.name);
0355         return ret;
0356     }
0357     ret = bcm2835_asb_disable(power, asb_m_reg);
0358     if (ret) {
0359         dev_warn(power->dev, "Failed to disable ASB master for %s\n",
0360              pd->base.name);
0361         bcm2835_asb_enable(power, asb_s_reg);
0362         return ret;
0363     }
0364 
0365     clk_disable_unprepare(pd->clk);
0366 
0367     /* Assert the resets. */
0368     PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags);
0369 
0370     return 0;
0371 }
0372 
0373 static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain)
0374 {
0375     struct bcm2835_power_domain *pd =
0376         container_of(domain, struct bcm2835_power_domain, base);
0377     struct bcm2835_power *power = pd->power;
0378 
0379     switch (pd->domain) {
0380     case BCM2835_POWER_DOMAIN_GRAFX:
0381         return bcm2835_power_power_on(pd, PM_GRAFX);
0382 
0383     case BCM2835_POWER_DOMAIN_GRAFX_V3D:
0384         return bcm2835_asb_power_on(pd, PM_GRAFX,
0385                         ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
0386                         PM_V3DRSTN);
0387 
0388     case BCM2835_POWER_DOMAIN_IMAGE:
0389         return bcm2835_power_power_on(pd, PM_IMAGE);
0390 
0391     case BCM2835_POWER_DOMAIN_IMAGE_PERI:
0392         return bcm2835_asb_power_on(pd, PM_IMAGE,
0393                         0, 0,
0394                         PM_PERIRSTN);
0395 
0396     case BCM2835_POWER_DOMAIN_IMAGE_ISP:
0397         return bcm2835_asb_power_on(pd, PM_IMAGE,
0398                         ASB_ISP_M_CTRL, ASB_ISP_S_CTRL,
0399                         PM_ISPRSTN);
0400 
0401     case BCM2835_POWER_DOMAIN_IMAGE_H264:
0402         return bcm2835_asb_power_on(pd, PM_IMAGE,
0403                         ASB_H264_M_CTRL, ASB_H264_S_CTRL,
0404                         PM_H264RSTN);
0405 
0406     case BCM2835_POWER_DOMAIN_USB:
0407         PM_WRITE(PM_USB, PM_USB_CTRLEN);
0408         return 0;
0409 
0410     case BCM2835_POWER_DOMAIN_DSI0:
0411         PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN);
0412         PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN);
0413         return 0;
0414 
0415     case BCM2835_POWER_DOMAIN_DSI1:
0416         PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN);
0417         PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN);
0418         return 0;
0419 
0420     case BCM2835_POWER_DOMAIN_CCP2TX:
0421         PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN);
0422         PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN);
0423         return 0;
0424 
0425     case BCM2835_POWER_DOMAIN_HDMI:
0426         PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR);
0427         PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN);
0428         PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD);
0429         usleep_range(100, 200);
0430         PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR);
0431         return 0;
0432 
0433     default:
0434         dev_err(power->dev, "Invalid domain %d\n", pd->domain);
0435         return -EINVAL;
0436     }
0437 }
0438 
0439 static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain)
0440 {
0441     struct bcm2835_power_domain *pd =
0442         container_of(domain, struct bcm2835_power_domain, base);
0443     struct bcm2835_power *power = pd->power;
0444 
0445     switch (pd->domain) {
0446     case BCM2835_POWER_DOMAIN_GRAFX:
0447         return bcm2835_power_power_off(pd, PM_GRAFX);
0448 
0449     case BCM2835_POWER_DOMAIN_GRAFX_V3D:
0450         return bcm2835_asb_power_off(pd, PM_GRAFX,
0451                          ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
0452                          PM_V3DRSTN);
0453 
0454     case BCM2835_POWER_DOMAIN_IMAGE:
0455         return bcm2835_power_power_off(pd, PM_IMAGE);
0456 
0457     case BCM2835_POWER_DOMAIN_IMAGE_PERI:
0458         return bcm2835_asb_power_off(pd, PM_IMAGE,
0459                          0, 0,
0460                          PM_PERIRSTN);
0461 
0462     case BCM2835_POWER_DOMAIN_IMAGE_ISP:
0463         return bcm2835_asb_power_off(pd, PM_IMAGE,
0464                          ASB_ISP_M_CTRL, ASB_ISP_S_CTRL,
0465                          PM_ISPRSTN);
0466 
0467     case BCM2835_POWER_DOMAIN_IMAGE_H264:
0468         return bcm2835_asb_power_off(pd, PM_IMAGE,
0469                          ASB_H264_M_CTRL, ASB_H264_S_CTRL,
0470                          PM_H264RSTN);
0471 
0472     case BCM2835_POWER_DOMAIN_USB:
0473         PM_WRITE(PM_USB, 0);
0474         return 0;
0475 
0476     case BCM2835_POWER_DOMAIN_DSI0:
0477         PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN);
0478         PM_WRITE(PM_DSI0, 0);
0479         return 0;
0480 
0481     case BCM2835_POWER_DOMAIN_DSI1:
0482         PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN);
0483         PM_WRITE(PM_DSI1, 0);
0484         return 0;
0485 
0486     case BCM2835_POWER_DOMAIN_CCP2TX:
0487         PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN);
0488         PM_WRITE(PM_CCP2TX, 0);
0489         return 0;
0490 
0491     case BCM2835_POWER_DOMAIN_HDMI:
0492         PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD);
0493         PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN);
0494         return 0;
0495 
0496     default:
0497         dev_err(power->dev, "Invalid domain %d\n", pd->domain);
0498         return -EINVAL;
0499     }
0500 }
0501 
0502 static int
0503 bcm2835_init_power_domain(struct bcm2835_power *power,
0504               int pd_xlate_index, const char *name)
0505 {
0506     struct device *dev = power->dev;
0507     struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index];
0508 
0509     dom->clk = devm_clk_get(dev->parent, name);
0510     if (IS_ERR(dom->clk)) {
0511         int ret = PTR_ERR(dom->clk);
0512 
0513         if (ret == -EPROBE_DEFER)
0514             return ret;
0515 
0516         /* Some domains don't have a clk, so make sure that we
0517          * don't deref an error pointer later.
0518          */
0519         dom->clk = NULL;
0520     }
0521 
0522     dom->base.name = name;
0523     dom->base.power_on = bcm2835_power_pd_power_on;
0524     dom->base.power_off = bcm2835_power_pd_power_off;
0525 
0526     dom->domain = pd_xlate_index;
0527     dom->power = power;
0528 
0529     /* XXX: on/off at boot? */
0530     pm_genpd_init(&dom->base, NULL, true);
0531 
0532     power->pd_xlate.domains[pd_xlate_index] = &dom->base;
0533 
0534     return 0;
0535 }
0536 
0537 /** bcm2835_reset_reset - Resets a block that has a reset line in the
0538  * PM block.
0539  *
0540  * The consumer of the reset controller must have the power domain up
0541  * -- there's no reset ability with the power domain down.  To reset
0542  * the sub-block, we just disable its access to memory through the
0543  * ASB, reset, and re-enable.
0544  */
0545 static int bcm2835_reset_reset(struct reset_controller_dev *rcdev,
0546                    unsigned long id)
0547 {
0548     struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power,
0549                            reset);
0550     struct bcm2835_power_domain *pd;
0551     int ret;
0552 
0553     switch (id) {
0554     case BCM2835_RESET_V3D:
0555         pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D];
0556         break;
0557     case BCM2835_RESET_H264:
0558         pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264];
0559         break;
0560     case BCM2835_RESET_ISP:
0561         pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP];
0562         break;
0563     default:
0564         dev_err(power->dev, "Bad reset id %ld\n", id);
0565         return -EINVAL;
0566     }
0567 
0568     ret = bcm2835_power_pd_power_off(&pd->base);
0569     if (ret)
0570         return ret;
0571 
0572     return bcm2835_power_pd_power_on(&pd->base);
0573 }
0574 
0575 static int bcm2835_reset_status(struct reset_controller_dev *rcdev,
0576                 unsigned long id)
0577 {
0578     struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power,
0579                            reset);
0580 
0581     switch (id) {
0582     case BCM2835_RESET_V3D:
0583         return !PM_READ(PM_GRAFX & PM_V3DRSTN);
0584     case BCM2835_RESET_H264:
0585         return !PM_READ(PM_IMAGE & PM_H264RSTN);
0586     case BCM2835_RESET_ISP:
0587         return !PM_READ(PM_IMAGE & PM_ISPRSTN);
0588     default:
0589         return -EINVAL;
0590     }
0591 }
0592 
0593 static const struct reset_control_ops bcm2835_reset_ops = {
0594     .reset = bcm2835_reset_reset,
0595     .status = bcm2835_reset_status,
0596 };
0597 
0598 static const char *const power_domain_names[] = {
0599     [BCM2835_POWER_DOMAIN_GRAFX] = "grafx",
0600     [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d",
0601 
0602     [BCM2835_POWER_DOMAIN_IMAGE] = "image",
0603     [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image",
0604     [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264",
0605     [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp",
0606 
0607     [BCM2835_POWER_DOMAIN_USB] = "usb",
0608     [BCM2835_POWER_DOMAIN_DSI0] = "dsi0",
0609     [BCM2835_POWER_DOMAIN_DSI1] = "dsi1",
0610     [BCM2835_POWER_DOMAIN_CAM0] = "cam0",
0611     [BCM2835_POWER_DOMAIN_CAM1] = "cam1",
0612     [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx",
0613     [BCM2835_POWER_DOMAIN_HDMI] = "hdmi",
0614 };
0615 
0616 static int bcm2835_power_probe(struct platform_device *pdev)
0617 {
0618     struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent);
0619     struct device *dev = &pdev->dev;
0620     struct bcm2835_power *power;
0621     static const struct {
0622         int parent, child;
0623     } domain_deps[] = {
0624         { BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D },
0625         { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI },
0626         { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 },
0627         { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP },
0628         { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB },
0629         { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 },
0630         { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 },
0631     };
0632     int ret = 0, i;
0633     u32 id;
0634 
0635     power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
0636     if (!power)
0637         return -ENOMEM;
0638     platform_set_drvdata(pdev, power);
0639 
0640     power->dev = dev;
0641     power->base = pm->base;
0642     power->asb = pm->asb;
0643     power->rpivid_asb = pm->rpivid_asb;
0644 
0645     id = readl(power->asb + ASB_AXI_BRDG_ID);
0646     if (id != BCM2835_BRDG_ID /* "BRDG" */) {
0647         dev_err(dev, "ASB register ID returned 0x%08x\n", id);
0648         return -ENODEV;
0649     }
0650 
0651     if (power->rpivid_asb) {
0652         id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
0653         if (id != BCM2835_BRDG_ID /* "BRDG" */) {
0654             dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
0655                      id);
0656             return -ENODEV;
0657         }
0658     }
0659 
0660     power->pd_xlate.domains = devm_kcalloc(dev,
0661                            ARRAY_SIZE(power_domain_names),
0662                            sizeof(*power->pd_xlate.domains),
0663                            GFP_KERNEL);
0664     if (!power->pd_xlate.domains)
0665         return -ENOMEM;
0666 
0667     power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names);
0668 
0669     for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) {
0670         ret = bcm2835_init_power_domain(power, i, power_domain_names[i]);
0671         if (ret)
0672             goto fail;
0673     }
0674 
0675     for (i = 0; i < ARRAY_SIZE(domain_deps); i++) {
0676         pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base,
0677                        &power->domains[domain_deps[i].child].base);
0678     }
0679 
0680     power->reset.owner = THIS_MODULE;
0681     power->reset.nr_resets = BCM2835_RESET_COUNT;
0682     power->reset.ops = &bcm2835_reset_ops;
0683     power->reset.of_node = dev->parent->of_node;
0684 
0685     ret = devm_reset_controller_register(dev, &power->reset);
0686     if (ret)
0687         goto fail;
0688 
0689     of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate);
0690 
0691     dev_info(dev, "Broadcom BCM2835 power domains driver");
0692     return 0;
0693 
0694 fail:
0695     for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) {
0696         struct generic_pm_domain *dom = &power->domains[i].base;
0697 
0698         if (dom->name)
0699             pm_genpd_remove(dom);
0700     }
0701     return ret;
0702 }
0703 
0704 static int bcm2835_power_remove(struct platform_device *pdev)
0705 {
0706     return 0;
0707 }
0708 
0709 static struct platform_driver bcm2835_power_driver = {
0710     .probe      = bcm2835_power_probe,
0711     .remove     = bcm2835_power_remove,
0712     .driver = {
0713         .name = "bcm2835-power",
0714     },
0715 };
0716 module_platform_driver(bcm2835_power_driver);
0717 
0718 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
0719 MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset");
0720 MODULE_LICENSE("GPL");