Merge tag 'arm-soc/for-6.6/drivers' of https://github.com/Broadcom/stblinux into...
authorArnd Bergmann <arnd@arndb.de>
Tue, 22 Aug 2023 02:11:58 +0000 (22:11 -0400)
committerArnd Bergmann <arnd@arndb.de>
Tue, 22 Aug 2023 02:15:14 +0000 (22:15 -0400)
This pull request contains Broadcom SoCs drivers changes for 6.6, please
pull the following:

- Rob updates the Broadcom SoC drivers to use the correct DT includes

* tag 'arm-soc/for-6.6/drivers' of https://github.com/Broadcom/stblinux:
  soc: bcm: Explicitly include correct DT includes

Link: https://lore.kernel.org/r/20230818164539.2871539-3-florian.fainelli@broadcom.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
1  2 
drivers/genpd/bcm/bcm-pmb.c
drivers/genpd/bcm/bcm63xx-power.c
drivers/genpd/bcm/raspberrypi-power.c

index 9407cac47fdbecf52fe7315fdd602538961fa3ee,0000000000000000000000000000000000000000..a72ba26ecf9d60782ceed2f594dbd459b90746d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,364 -1,0 +1,363 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +/*
 + * Copyright (c) 2013 Broadcom
 + * Copyright (C) 2020 Rafał Miłecki <rafal@milecki.pl>
 + */
 +
 +#include <dt-bindings/soc/bcm-pmb.h>
 +#include <linux/io.h>
 +#include <linux/module.h>
 +#include <linux/of.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <linux/reset/bcm63xx_pmb.h>
 +
 +#define BPCM_ID_REG                                   0x00
 +#define BPCM_CAPABILITIES                             0x04
 +#define  BPCM_CAP_NUM_ZONES                           0x000000ff
 +#define  BPCM_CAP_SR_REG_BITS                         0x0000ff00
 +#define  BPCM_CAP_PLLTYPE                             0x00030000
 +#define  BPCM_CAP_UBUS                                        0x00080000
 +#define BPCM_CONTROL                                  0x08
 +#define BPCM_STATUS                                   0x0c
 +#define BPCM_ROSC_CONTROL                             0x10
 +#define BPCM_ROSC_THRESH_H                            0x14
 +#define BPCM_ROSC_THRESHOLD_BCM6838                   0x14
 +#define BPCM_ROSC_THRESH_S                            0x18
 +#define BPCM_ROSC_COUNT_BCM6838                               0x18
 +#define BPCM_ROSC_COUNT                                       0x1c
 +#define BPCM_PWD_CONTROL_BCM6838                      0x1c
 +#define BPCM_PWD_CONTROL                              0x20
 +#define BPCM_SR_CONTROL_BCM6838                               0x20
 +#define BPCM_PWD_ACCUM_CONTROL                                0x24
 +#define BPCM_SR_CONTROL                                       0x28
 +#define BPCM_GLOBAL_CONTROL                           0x2c
 +#define BPCM_MISC_CONTROL                             0x30
 +#define BPCM_MISC_CONTROL2                            0x34
 +#define BPCM_SGPHY_CNTL                                       0x38
 +#define BPCM_SGPHY_STATUS                             0x3c
 +#define BPCM_ZONE0                                    0x40
 +#define  BPCM_ZONE_CONTROL                            0x00
 +#define   BPCM_ZONE_CONTROL_MANUAL_CLK_EN             0x00000001
 +#define   BPCM_ZONE_CONTROL_MANUAL_RESET_CTL          0x00000002
 +#define   BPCM_ZONE_CONTROL_FREQ_SCALE_USED           0x00000004      /* R/O */
 +#define   BPCM_ZONE_CONTROL_DPG_CAPABLE                       0x00000008      /* R/O */
 +#define   BPCM_ZONE_CONTROL_MANUAL_MEM_PWR            0x00000030
 +#define   BPCM_ZONE_CONTROL_MANUAL_ISO_CTL            0x00000040
 +#define   BPCM_ZONE_CONTROL_MANUAL_CTL                        0x00000080
 +#define   BPCM_ZONE_CONTROL_DPG_CTL_EN                        0x00000100
 +#define   BPCM_ZONE_CONTROL_PWR_DN_REQ                        0x00000200
 +#define   BPCM_ZONE_CONTROL_PWR_UP_REQ                        0x00000400
 +#define   BPCM_ZONE_CONTROL_MEM_PWR_CTL_EN            0x00000800
 +#define   BPCM_ZONE_CONTROL_BLK_RESET_ASSERT          0x00001000
 +#define   BPCM_ZONE_CONTROL_MEM_STBY                  0x00002000
 +#define   BPCM_ZONE_CONTROL_RESERVED                  0x0007c000
 +#define   BPCM_ZONE_CONTROL_PWR_CNTL_STATE            0x00f80000
 +#define   BPCM_ZONE_CONTROL_FREQ_SCALAR_DYN_SEL               0x01000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_PWR_OFF_STATE             0x02000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_PWR_ON_STATE              0x04000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_PWR_GOOD                  0x08000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_DPG_PWR_STATE             0x10000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_MEM_PWR_STATE             0x20000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_ISO_STATE                 0x40000000      /* R/O */
 +#define   BPCM_ZONE_CONTROL_RESET_STATE                       0x80000000      /* R/O */
 +#define  BPCM_ZONE_CONFIG1                            0x04
 +#define  BPCM_ZONE_CONFIG2                            0x08
 +#define  BPCM_ZONE_FREQ_SCALAR_CONTROL                        0x0c
 +#define  BPCM_ZONE_SIZE                                       0x10
 +
 +struct bcm_pmb {
 +      struct device *dev;
 +      void __iomem *base;
 +      spinlock_t lock;
 +      bool little_endian;
 +      struct genpd_onecell_data genpd_onecell_data;
 +};
 +
 +struct bcm_pmb_pd_data {
 +      const char * const name;
 +      int id;
 +      u8 bus;
 +      u8 device;
 +};
 +
 +struct bcm_pmb_pm_domain {
 +      struct bcm_pmb *pmb;
 +      const struct bcm_pmb_pd_data *data;
 +      struct generic_pm_domain genpd;
 +};
 +
 +static int bcm_pmb_bpcm_read(struct bcm_pmb *pmb, int bus, u8 device,
 +                           int offset, u32 *val)
 +{
 +      void __iomem *base = pmb->base + bus * 0x20;
 +      unsigned long flags;
 +      int err;
 +
 +      spin_lock_irqsave(&pmb->lock, flags);
 +      err = bpcm_rd(base, device, offset, val);
 +      spin_unlock_irqrestore(&pmb->lock, flags);
 +
 +      if (!err)
 +              *val = pmb->little_endian ? le32_to_cpu(*val) : be32_to_cpu(*val);
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_bpcm_write(struct bcm_pmb *pmb, int bus, u8 device,
 +                            int offset, u32 val)
 +{
 +      void __iomem *base = pmb->base + bus * 0x20;
 +      unsigned long flags;
 +      int err;
 +
 +      val = pmb->little_endian ? cpu_to_le32(val) : cpu_to_be32(val);
 +
 +      spin_lock_irqsave(&pmb->lock, flags);
 +      err = bpcm_wr(base, device, offset, val);
 +      spin_unlock_irqrestore(&pmb->lock, flags);
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_power_off_zone(struct bcm_pmb *pmb, int bus, u8 device,
 +                                int zone)
 +{
 +      int offset;
 +      u32 val;
 +      int err;
 +
 +      offset = BPCM_ZONE0 + zone * BPCM_ZONE_SIZE + BPCM_ZONE_CONTROL;
 +
 +      err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val);
 +      if (err)
 +              return err;
 +
 +      val |= BPCM_ZONE_CONTROL_PWR_DN_REQ;
 +      val &= ~BPCM_ZONE_CONTROL_PWR_UP_REQ;
 +
 +      err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val);
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_power_on_zone(struct bcm_pmb *pmb, int bus, u8 device,
 +                               int zone)
 +{
 +      int offset;
 +      u32 val;
 +      int err;
 +
 +      offset = BPCM_ZONE0 + zone * BPCM_ZONE_SIZE + BPCM_ZONE_CONTROL;
 +
 +      err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val);
 +      if (err)
 +              return err;
 +
 +      if (!(val & BPCM_ZONE_CONTROL_PWR_ON_STATE)) {
 +              val &= ~BPCM_ZONE_CONTROL_PWR_DN_REQ;
 +              val |= BPCM_ZONE_CONTROL_DPG_CTL_EN;
 +              val |= BPCM_ZONE_CONTROL_PWR_UP_REQ;
 +              val |= BPCM_ZONE_CONTROL_MEM_PWR_CTL_EN;
 +              val |= BPCM_ZONE_CONTROL_BLK_RESET_ASSERT;
 +
 +              err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val);
 +      }
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_power_off_device(struct bcm_pmb *pmb, int bus, u8 device)
 +{
 +      int offset;
 +      u32 val;
 +      int err;
 +
 +      /* Entire device can be powered off by powering off the 0th zone */
 +      offset = BPCM_ZONE0 + BPCM_ZONE_CONTROL;
 +
 +      err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val);
 +      if (err)
 +              return err;
 +
 +      if (!(val & BPCM_ZONE_CONTROL_PWR_OFF_STATE)) {
 +              val = BPCM_ZONE_CONTROL_PWR_DN_REQ;
 +
 +              err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val);
 +      }
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_power_on_device(struct bcm_pmb *pmb, int bus, u8 device)
 +{
 +      u32 val;
 +      int err;
 +      int i;
 +
 +      err = bcm_pmb_bpcm_read(pmb, bus, device, BPCM_CAPABILITIES, &val);
 +      if (err)
 +              return err;
 +
 +      for (i = 0; i < (val & BPCM_CAP_NUM_ZONES); i++) {
 +              err = bcm_pmb_power_on_zone(pmb, bus, device, i);
 +              if (err)
 +                      return err;
 +      }
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_power_on_sata(struct bcm_pmb *pmb, int bus, u8 device)
 +{
 +      int err;
 +
 +      err = bcm_pmb_power_on_zone(pmb, bus, device, 0);
 +      if (err)
 +              return err;
 +
 +      /* Does not apply to the BCM963158 */
 +      err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_MISC_CONTROL, 0);
 +      if (err)
 +              return err;
 +
 +      err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_SR_CONTROL, 0xffffffff);
 +      if (err)
 +              return err;
 +
 +      err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_SR_CONTROL, 0);
 +
 +      return err;
 +}
 +
 +static int bcm_pmb_power_on(struct generic_pm_domain *genpd)
 +{
 +      struct bcm_pmb_pm_domain *pd = container_of(genpd, struct bcm_pmb_pm_domain, genpd);
 +      const struct bcm_pmb_pd_data *data = pd->data;
 +      struct bcm_pmb *pmb = pd->pmb;
 +
 +      switch (data->id) {
 +      case BCM_PMB_PCIE0:
 +      case BCM_PMB_PCIE1:
 +      case BCM_PMB_PCIE2:
 +              return bcm_pmb_power_on_zone(pmb, data->bus, data->device, 0);
 +      case BCM_PMB_HOST_USB:
 +              return bcm_pmb_power_on_device(pmb, data->bus, data->device);
 +      case BCM_PMB_SATA:
 +              return bcm_pmb_power_on_sata(pmb, data->bus, data->device);
 +      default:
 +              dev_err(pmb->dev, "unsupported device id: %d\n", data->id);
 +              return -EINVAL;
 +      }
 +}
 +
 +static int bcm_pmb_power_off(struct generic_pm_domain *genpd)
 +{
 +      struct bcm_pmb_pm_domain *pd = container_of(genpd, struct bcm_pmb_pm_domain, genpd);
 +      const struct bcm_pmb_pd_data *data = pd->data;
 +      struct bcm_pmb *pmb = pd->pmb;
 +
 +      switch (data->id) {
 +      case BCM_PMB_PCIE0:
 +      case BCM_PMB_PCIE1:
 +      case BCM_PMB_PCIE2:
 +              return bcm_pmb_power_off_zone(pmb, data->bus, data->device, 0);
 +      case BCM_PMB_HOST_USB:
 +              return bcm_pmb_power_off_device(pmb, data->bus, data->device);
 +      default:
 +              dev_err(pmb->dev, "unsupported device id: %d\n", data->id);
 +              return -EINVAL;
 +      }
 +}
 +
 +static int bcm_pmb_probe(struct platform_device *pdev)
 +{
 +      struct device *dev = &pdev->dev;
 +      const struct bcm_pmb_pd_data *table;
 +      const struct bcm_pmb_pd_data *e;
 +      struct bcm_pmb *pmb;
 +      int max_id;
 +      int err;
 +
 +      pmb = devm_kzalloc(dev, sizeof(*pmb), GFP_KERNEL);
 +      if (!pmb)
 +              return -ENOMEM;
 +
 +      pmb->dev = dev;
 +
 +      pmb->base = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(pmb->base))
 +              return PTR_ERR(pmb->base);
 +
 +      spin_lock_init(&pmb->lock);
 +
 +      pmb->little_endian = !of_device_is_big_endian(dev->of_node);
 +
 +      table = of_device_get_match_data(dev);
 +      if (!table)
 +              return -EINVAL;
 +
 +      max_id = 0;
 +      for (e = table; e->name; e++)
 +              max_id = max(max_id, e->id);
 +
 +      pmb->genpd_onecell_data.num_domains = max_id + 1;
 +      pmb->genpd_onecell_data.domains =
 +              devm_kcalloc(dev, pmb->genpd_onecell_data.num_domains,
 +                           sizeof(struct generic_pm_domain *), GFP_KERNEL);
 +      if (!pmb->genpd_onecell_data.domains)
 +              return -ENOMEM;
 +
 +      for (e = table; e->name; e++) {
 +              struct bcm_pmb_pm_domain *pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
 +
 +              if (!pd)
 +                      return -ENOMEM;
 +
 +              pd->pmb = pmb;
 +              pd->data = e;
 +              pd->genpd.name = e->name;
 +              pd->genpd.power_on = bcm_pmb_power_on;
 +              pd->genpd.power_off = bcm_pmb_power_off;
 +
 +              pm_genpd_init(&pd->genpd, NULL, true);
 +              pmb->genpd_onecell_data.domains[e->id] = &pd->genpd;
 +      }
 +
 +      err = of_genpd_add_provider_onecell(dev->of_node, &pmb->genpd_onecell_data);
 +      if (err) {
 +              dev_err(dev, "failed to add genpd provider: %d\n", err);
 +              return err;
 +      }
 +
 +      return 0;
 +}
 +
 +static const struct bcm_pmb_pd_data bcm_pmb_bcm4908_data[] = {
 +      { .name = "pcie2", .id = BCM_PMB_PCIE2, .bus = 0, .device = 2, },
 +      { .name = "pcie0", .id = BCM_PMB_PCIE0, .bus = 1, .device = 14, },
 +      { .name = "pcie1", .id = BCM_PMB_PCIE1, .bus = 1, .device = 15, },
 +      { .name = "usb", .id = BCM_PMB_HOST_USB, .bus = 1, .device = 17, },
 +      { },
 +};
 +
 +static const struct bcm_pmb_pd_data bcm_pmb_bcm63138_data[] = {
 +      { .name = "sata", .id = BCM_PMB_SATA, .bus = 0, .device = 3, },
 +      { },
 +};
 +
 +static const struct of_device_id bcm_pmb_of_match[] = {
 +      { .compatible = "brcm,bcm4908-pmb", .data = &bcm_pmb_bcm4908_data, },
 +      { .compatible = "brcm,bcm63138-pmb", .data = &bcm_pmb_bcm63138_data, },
 +      { },
 +};
 +
 +static struct platform_driver bcm_pmb_driver = {
 +      .driver = {
 +              .name = "bcm-pmb",
 +              .of_match_table = bcm_pmb_of_match,
 +      },
 +      .probe  = bcm_pmb_probe,
 +};
 +
 +builtin_platform_driver(bcm_pmb_driver);
index aa72e13d5d0e005781d557a5ba9eb45b8feb6afc,0000000000000000000000000000000000000000..98b0c2430dbc6bdc2cf14fa757178af1213e66bf
mode 100644,000000..100644
--- /dev/null
@@@ -1,376 -1,0 +1,375 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +/*
 + * BCM63xx Power Domain Controller Driver
 + *
 + * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
 + */
 +
 +#include <dt-bindings/soc/bcm6318-pm.h>
 +#include <dt-bindings/soc/bcm6328-pm.h>
 +#include <dt-bindings/soc/bcm6362-pm.h>
 +#include <dt-bindings/soc/bcm63268-pm.h>
 +#include <linux/io.h>
 +#include <linux/module.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <linux/of.h>
 +
 +struct bcm63xx_power_dev {
 +      struct generic_pm_domain genpd;
 +      struct bcm63xx_power *power;
 +      uint32_t mask;
 +};
 +
 +struct bcm63xx_power {
 +      void __iomem *base;
 +      spinlock_t lock;
 +      struct bcm63xx_power_dev *dev;
 +      struct genpd_onecell_data genpd_data;
 +      struct generic_pm_domain **genpd;
 +};
 +
 +struct bcm63xx_power_data {
 +      const char * const name;
 +      uint8_t bit;
 +      unsigned int flags;
 +};
 +
 +static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on)
 +{
 +      struct bcm63xx_power *power = pmd->power;
 +
 +      if (!pmd->mask) {
 +              *is_on = false;
 +              return -EINVAL;
 +      }
 +
 +      *is_on = !(__raw_readl(power->base) & pmd->mask);
 +
 +      return 0;
 +}
 +
 +static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on)
 +{
 +      struct bcm63xx_power *power = pmd->power;
 +      unsigned long flags;
 +      uint32_t val;
 +
 +      if (!pmd->mask)
 +              return -EINVAL;
 +
 +      spin_lock_irqsave(&power->lock, flags);
 +      val = __raw_readl(power->base);
 +      if (on)
 +              val &= ~pmd->mask;
 +      else
 +              val |= pmd->mask;
 +      __raw_writel(val, power->base);
 +      spin_unlock_irqrestore(&power->lock, flags);
 +
 +      return 0;
 +}
 +
 +static int bcm63xx_power_on(struct generic_pm_domain *genpd)
 +{
 +      struct bcm63xx_power_dev *pmd = container_of(genpd,
 +              struct bcm63xx_power_dev, genpd);
 +
 +      return bcm63xx_power_set_state(pmd, true);
 +}
 +
 +static int bcm63xx_power_off(struct generic_pm_domain *genpd)
 +{
 +      struct bcm63xx_power_dev *pmd = container_of(genpd,
 +              struct bcm63xx_power_dev, genpd);
 +
 +      return bcm63xx_power_set_state(pmd, false);
 +}
 +
 +static int bcm63xx_power_probe(struct platform_device *pdev)
 +{
 +      struct device *dev = &pdev->dev;
 +      struct device_node *np = dev->of_node;
 +      const struct bcm63xx_power_data *entry, *table;
 +      struct bcm63xx_power *power;
 +      unsigned int ndom;
 +      uint8_t max_bit = 0;
 +      int ret;
 +
 +      power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
 +      if (!power)
 +              return -ENOMEM;
 +
 +      power->base = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(power->base))
 +              return PTR_ERR(power->base);
 +
 +      table = of_device_get_match_data(dev);
 +      if (!table)
 +              return -EINVAL;
 +
 +      power->genpd_data.num_domains = 0;
 +      ndom = 0;
 +      for (entry = table; entry->name; entry++) {
 +              max_bit = max(max_bit, entry->bit);
 +              ndom++;
 +      }
 +
 +      if (!ndom)
 +              return -ENODEV;
 +
 +      power->genpd_data.num_domains = max_bit + 1;
 +
 +      power->dev = devm_kcalloc(dev, power->genpd_data.num_domains,
 +                                sizeof(struct bcm63xx_power_dev),
 +                                GFP_KERNEL);
 +      if (!power->dev)
 +              return -ENOMEM;
 +
 +      power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains,
 +                                  sizeof(struct generic_pm_domain *),
 +                                  GFP_KERNEL);
 +      if (!power->genpd)
 +              return -ENOMEM;
 +
 +      power->genpd_data.domains = power->genpd;
 +
 +      ndom = 0;
 +      for (entry = table; entry->name; entry++) {
 +              struct bcm63xx_power_dev *pmd = &power->dev[ndom];
 +              bool is_on;
 +
 +              pmd->power = power;
 +              pmd->mask = BIT(entry->bit);
 +              pmd->genpd.name = entry->name;
 +              pmd->genpd.flags = entry->flags;
 +
 +              ret = bcm63xx_power_get_state(pmd, &is_on);
 +              if (ret)
 +                      dev_warn(dev, "unable to get current state for %s\n",
 +                               pmd->genpd.name);
 +
 +              pmd->genpd.power_on = bcm63xx_power_on;
 +              pmd->genpd.power_off = bcm63xx_power_off;
 +
 +              pm_genpd_init(&pmd->genpd, NULL, !is_on);
 +              power->genpd[entry->bit] = &pmd->genpd;
 +
 +              ndom++;
 +      }
 +
 +      spin_lock_init(&power->lock);
 +
 +      ret = of_genpd_add_provider_onecell(np, &power->genpd_data);
 +      if (ret) {
 +              dev_err(dev, "failed to register genpd driver: %d\n", ret);
 +              return ret;
 +      }
 +
 +      dev_info(dev, "registered %u power domains\n", ndom);
 +
 +      return 0;
 +}
 +
 +static const struct bcm63xx_power_data bcm6318_power_domains[] = {
 +      {
 +              .name = "pcie",
 +              .bit = BCM6318_POWER_DOMAIN_PCIE,
 +      }, {
 +              .name = "usb",
 +              .bit = BCM6318_POWER_DOMAIN_USB,
 +      }, {
 +              .name = "ephy0",
 +              .bit = BCM6318_POWER_DOMAIN_EPHY0,
 +      }, {
 +              .name = "ephy1",
 +              .bit = BCM6318_POWER_DOMAIN_EPHY1,
 +      }, {
 +              .name = "ephy2",
 +              .bit = BCM6318_POWER_DOMAIN_EPHY2,
 +      }, {
 +              .name = "ephy3",
 +              .bit = BCM6318_POWER_DOMAIN_EPHY3,
 +      }, {
 +              .name = "ldo2p5",
 +              .bit = BCM6318_POWER_DOMAIN_LDO2P5,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "ldo2p9",
 +              .bit = BCM6318_POWER_DOMAIN_LDO2P9,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "sw1p0",
 +              .bit = BCM6318_POWER_DOMAIN_SW1P0,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "pad",
 +              .bit = BCM6318_POWER_DOMAIN_PAD,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              /* sentinel */
 +      },
 +};
 +
 +static const struct bcm63xx_power_data bcm6328_power_domains[] = {
 +      {
 +              .name = "adsl2-mips",
 +              .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS,
 +      }, {
 +              .name = "adsl2-phy",
 +              .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY,
 +      }, {
 +              .name = "adsl2-afe",
 +              .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE,
 +      }, {
 +              .name = "sar",
 +              .bit = BCM6328_POWER_DOMAIN_SAR,
 +      }, {
 +              .name = "pcm",
 +              .bit = BCM6328_POWER_DOMAIN_PCM,
 +      }, {
 +              .name = "usbd",
 +              .bit = BCM6328_POWER_DOMAIN_USBD,
 +      }, {
 +              .name = "usbh",
 +              .bit = BCM6328_POWER_DOMAIN_USBH,
 +      }, {
 +              .name = "pcie",
 +              .bit = BCM6328_POWER_DOMAIN_PCIE,
 +      }, {
 +              .name = "robosw",
 +              .bit = BCM6328_POWER_DOMAIN_ROBOSW,
 +      }, {
 +              .name = "ephy",
 +              .bit = BCM6328_POWER_DOMAIN_EPHY,
 +      }, {
 +              /* sentinel */
 +      },
 +};
 +
 +static const struct bcm63xx_power_data bcm6362_power_domains[] = {
 +      {
 +              .name = "sar",
 +              .bit = BCM6362_POWER_DOMAIN_SAR,
 +      }, {
 +              .name = "ipsec",
 +              .bit = BCM6362_POWER_DOMAIN_IPSEC,
 +      }, {
 +              .name = "mips",
 +              .bit = BCM6362_POWER_DOMAIN_MIPS,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "dect",
 +              .bit = BCM6362_POWER_DOMAIN_DECT,
 +      }, {
 +              .name = "usbh",
 +              .bit = BCM6362_POWER_DOMAIN_USBH,
 +      }, {
 +              .name = "usbd",
 +              .bit = BCM6362_POWER_DOMAIN_USBD,
 +      }, {
 +              .name = "robosw",
 +              .bit = BCM6362_POWER_DOMAIN_ROBOSW,
 +      }, {
 +              .name = "pcm",
 +              .bit = BCM6362_POWER_DOMAIN_PCM,
 +      }, {
 +              .name = "periph",
 +              .bit = BCM6362_POWER_DOMAIN_PERIPH,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "adsl-phy",
 +              .bit = BCM6362_POWER_DOMAIN_ADSL_PHY,
 +      }, {
 +              .name = "gmii-pads",
 +              .bit = BCM6362_POWER_DOMAIN_GMII_PADS,
 +      }, {
 +              .name = "fap",
 +              .bit = BCM6362_POWER_DOMAIN_FAP,
 +      }, {
 +              .name = "pcie",
 +              .bit = BCM6362_POWER_DOMAIN_PCIE,
 +      }, {
 +              .name = "wlan-pads",
 +              .bit = BCM6362_POWER_DOMAIN_WLAN_PADS,
 +      }, {
 +              /* sentinel */
 +      },
 +};
 +
 +static const struct bcm63xx_power_data bcm63268_power_domains[] = {
 +      {
 +              .name = "sar",
 +              .bit = BCM63268_POWER_DOMAIN_SAR,
 +      }, {
 +              .name = "ipsec",
 +              .bit = BCM63268_POWER_DOMAIN_IPSEC,
 +      }, {
 +              .name = "mips",
 +              .bit = BCM63268_POWER_DOMAIN_MIPS,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "dect",
 +              .bit = BCM63268_POWER_DOMAIN_DECT,
 +      }, {
 +              .name = "usbh",
 +              .bit = BCM63268_POWER_DOMAIN_USBH,
 +      }, {
 +              .name = "usbd",
 +              .bit = BCM63268_POWER_DOMAIN_USBD,
 +      }, {
 +              .name = "robosw",
 +              .bit = BCM63268_POWER_DOMAIN_ROBOSW,
 +      }, {
 +              .name = "pcm",
 +              .bit = BCM63268_POWER_DOMAIN_PCM,
 +      }, {
 +              .name = "periph",
 +              .bit = BCM63268_POWER_DOMAIN_PERIPH,
 +              .flags = GENPD_FLAG_ALWAYS_ON,
 +      }, {
 +              .name = "vdsl-phy",
 +              .bit = BCM63268_POWER_DOMAIN_VDSL_PHY,
 +      }, {
 +              .name = "vdsl-mips",
 +              .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS,
 +      }, {
 +              .name = "fap",
 +              .bit = BCM63268_POWER_DOMAIN_FAP,
 +      }, {
 +              .name = "pcie",
 +              .bit = BCM63268_POWER_DOMAIN_PCIE,
 +      }, {
 +              .name = "wlan-pads",
 +              .bit = BCM63268_POWER_DOMAIN_WLAN_PADS,
 +      }, {
 +              /* sentinel */
 +      },
 +};
 +
 +static const struct of_device_id bcm63xx_power_of_match[] = {
 +      {
 +              .compatible = "brcm,bcm6318-power-controller",
 +              .data = &bcm6318_power_domains,
 +      }, {
 +              .compatible = "brcm,bcm6328-power-controller",
 +              .data = &bcm6328_power_domains,
 +      }, {
 +              .compatible = "brcm,bcm6362-power-controller",
 +              .data = &bcm6362_power_domains,
 +      }, {
 +              .compatible = "brcm,bcm63268-power-controller",
 +              .data = &bcm63268_power_domains,
 +      }, {
 +              /* sentinel */
 +      }
 +};
 +
 +static struct platform_driver bcm63xx_power_driver = {
 +      .driver = {
 +              .name = "bcm63xx-power-controller",
 +              .of_match_table = bcm63xx_power_of_match,
 +      },
 +      .probe  = bcm63xx_power_probe,
 +};
 +builtin_platform_driver(bcm63xx_power_driver);
index 58175af982a0dc836c07a1fc229508095967c90b,0000000000000000000000000000000000000000..06196ebfe03b03aba86b4e42f61f09950bbf1f6c
mode 100644,000000..100644
--- /dev/null
@@@ -1,245 -1,0 +1,245 @@@
- #include <linux/of_platform.h>
 +// SPDX-License-Identifier: GPL-2.0
 +/* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
 + *
 + * Authors:
 + * Alexander Aring <aar@pengutronix.de>
 + * Eric Anholt <eric@anholt.net>
 + */
 +
 +#include <linux/module.h>
++#include <linux/of.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <dt-bindings/power/raspberrypi-power.h>
 +#include <soc/bcm2835/raspberrypi-firmware.h>
 +
 +/*
 + * Firmware indices for the old power domains interface.  Only a few
 + * of them were actually implemented.
 + */
 +#define RPI_OLD_POWER_DOMAIN_USB              3
 +#define RPI_OLD_POWER_DOMAIN_V3D              10
 +
 +struct rpi_power_domain {
 +      u32 domain;
 +      bool enabled;
 +      bool old_interface;
 +      struct generic_pm_domain base;
 +      struct rpi_firmware *fw;
 +};
 +
 +struct rpi_power_domains {
 +      bool has_new_interface;
 +      struct genpd_onecell_data xlate;
 +      struct rpi_firmware *fw;
 +      struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT];
 +};
 +
 +/*
 + * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and
 + * RPI_FIRMWARE_SET_DOMAIN_STATE
 + */
 +struct rpi_power_domain_packet {
 +      u32 domain;
 +      u32 on;
 +};
 +
 +/*
 + * Asks the firmware to enable or disable power on a specific power
 + * domain.
 + */
 +static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on)
 +{
 +      struct rpi_power_domain_packet packet;
 +
 +      packet.domain = rpi_domain->domain;
 +      packet.on = on;
 +      return rpi_firmware_property(rpi_domain->fw,
 +                                   rpi_domain->old_interface ?
 +                                   RPI_FIRMWARE_SET_POWER_STATE :
 +                                   RPI_FIRMWARE_SET_DOMAIN_STATE,
 +                                   &packet, sizeof(packet));
 +}
 +
 +static int rpi_domain_off(struct generic_pm_domain *domain)
 +{
 +      struct rpi_power_domain *rpi_domain =
 +              container_of(domain, struct rpi_power_domain, base);
 +
 +      return rpi_firmware_set_power(rpi_domain, false);
 +}
 +
 +static int rpi_domain_on(struct generic_pm_domain *domain)
 +{
 +      struct rpi_power_domain *rpi_domain =
 +              container_of(domain, struct rpi_power_domain, base);
 +
 +      return rpi_firmware_set_power(rpi_domain, true);
 +}
 +
 +static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains,
 +                                       int xlate_index, const char *name)
 +{
 +      struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
 +
 +      dom->fw = rpi_domains->fw;
 +
 +      dom->base.name = name;
 +      dom->base.power_on = rpi_domain_on;
 +      dom->base.power_off = rpi_domain_off;
 +
 +      /*
 +       * Treat all power domains as off at boot.
 +       *
 +       * The firmware itself may be keeping some domains on, but
 +       * from Linux's perspective all we control is the refcounts
 +       * that we give to the firmware, and we can't ask the firmware
 +       * to turn off something that we haven't ourselves turned on.
 +       */
 +      pm_genpd_init(&dom->base, NULL, true);
 +
 +      rpi_domains->xlate.domains[xlate_index] = &dom->base;
 +}
 +
 +static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains,
 +                                int xlate_index, const char *name)
 +{
 +      struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
 +
 +      if (!rpi_domains->has_new_interface)
 +              return;
 +
 +      /* The DT binding index is the firmware's domain index minus one. */
 +      dom->domain = xlate_index + 1;
 +
 +      rpi_common_init_power_domain(rpi_domains, xlate_index, name);
 +}
 +
 +static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains,
 +                                    int xlate_index, int domain,
 +                                    const char *name)
 +{
 +      struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
 +
 +      dom->old_interface = true;
 +      dom->domain = domain;
 +
 +      rpi_common_init_power_domain(rpi_domains, xlate_index, name);
 +}
 +
 +/*
 + * Detects whether the firmware supports the new power domains interface.
 + *
 + * The firmware doesn't actually return an error on an unknown tag,
 + * and just skips over it, so we do the detection by putting an
 + * unexpected value in the return field and checking if it was
 + * unchanged.
 + */
 +static bool
 +rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains)
 +{
 +      struct rpi_power_domain_packet packet;
 +      int ret;
 +
 +      packet.domain = RPI_POWER_DOMAIN_ARM;
 +      packet.on = ~0;
 +
 +      ret = rpi_firmware_property(rpi_domains->fw,
 +                                  RPI_FIRMWARE_GET_DOMAIN_STATE,
 +                                  &packet, sizeof(packet));
 +
 +      return ret == 0 && packet.on != ~0;
 +}
 +
 +static int rpi_power_probe(struct platform_device *pdev)
 +{
 +      struct device_node *fw_np;
 +      struct device *dev = &pdev->dev;
 +      struct rpi_power_domains *rpi_domains;
 +
 +      rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL);
 +      if (!rpi_domains)
 +              return -ENOMEM;
 +
 +      rpi_domains->xlate.domains =
 +              devm_kcalloc(dev,
 +                           RPI_POWER_DOMAIN_COUNT,
 +                           sizeof(*rpi_domains->xlate.domains),
 +                           GFP_KERNEL);
 +      if (!rpi_domains->xlate.domains)
 +              return -ENOMEM;
 +
 +      rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT;
 +
 +      fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
 +      if (!fw_np) {
 +              dev_err(&pdev->dev, "no firmware node\n");
 +              return -ENODEV;
 +      }
 +
 +      rpi_domains->fw = devm_rpi_firmware_get(&pdev->dev, fw_np);
 +      of_node_put(fw_np);
 +      if (!rpi_domains->fw)
 +              return -EPROBE_DEFER;
 +
 +      rpi_domains->has_new_interface =
 +              rpi_has_new_domain_support(rpi_domains);
 +
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER,
 +                            "VIDEO_SCALER");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI");
 +
 +      /*
 +       * Use the old firmware interface for USB power, so that we
 +       * can turn it on even if the firmware hasn't been updated.
 +       */
 +      rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB,
 +                                RPI_OLD_POWER_DOMAIN_USB, "USB");
 +
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER,
 +                            "TRANSPOSER");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP");
 +      rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM");
 +
 +      of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate);
 +
 +      platform_set_drvdata(pdev, rpi_domains);
 +
 +      return 0;
 +}
 +
 +static const struct of_device_id rpi_power_of_match[] = {
 +      { .compatible = "raspberrypi,bcm2835-power", },
 +      {},
 +};
 +MODULE_DEVICE_TABLE(of, rpi_power_of_match);
 +
 +static struct platform_driver rpi_power_driver = {
 +      .driver = {
 +              .name = "raspberrypi-power",
 +              .of_match_table = rpi_power_of_match,
 +      },
 +      .probe          = rpi_power_probe,
 +};
 +builtin_platform_driver(rpi_power_driver);
 +
 +MODULE_AUTHOR("Alexander Aring <aar@pengutronix.de>");
 +MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 +MODULE_DESCRIPTION("Raspberry Pi power domain driver");