Merge tag 'imx-drivers-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo...
authorArnd Bergmann <arnd@arndb.de>
Mon, 14 Aug 2023 16:00:57 +0000 (18:00 +0200)
committerArnd Bergmann <arnd@arndb.de>
Mon, 14 Aug 2023 16:01:15 +0000 (18:01 +0200)
i.MX drivers update for 6.6:

- A series from NXP i.MX developers (Peng Fan, etc.) to update imx-scu
  and imx-scu-irq firmware drivers.
- Add dedicated lockdep class for nested genpd locks to fix a lockdep
  warning in imx93-blk-ctrl driver.
- A change from Rob to explicitly include correct DT headers for i.MX
  SoC drivers.
- Use devm_platform_ioremap_resource() in imx-weim bus driver.

* tag 'imx-drivers-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  firmware: imx: scu-irq: support identifying SCU wakeup source from sysfs
  firmware: imx: scu-irq: enlarge the IMX_SC_IRQ_NUM_GROUP
  firmware: imx: scu-irq: add imx_scu_irq_get_status
  firmware: imx: scu-irq: fix RCU complaint after M4 partition reset
  firmware: imx: scu: use EOPNOTSUPP
  firmware: imx: scu: use soc name for soc_id
  firmware: imx: scu: increase RPC timeout
  firmware: imx: scu: change init level to subsys_initcall_sync
  soc: imx: Explicitly include correct DT includes
  bus: imx-weim: use devm_platform_ioremap_resource
  soc: imx: imx93-blk-ctrl: Add dedicated lockdep class for nested genpd locks

Link: https://lore.kernel.org/r/20230813133354.847010-1-shawnguo@kernel.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
1  2 
drivers/genpd/imx/gpcv2.c
drivers/genpd/imx/imx8m-blk-ctrl.c
drivers/genpd/imx/imx8mp-blk-ctrl.c
drivers/genpd/imx/imx93-blk-ctrl.c
drivers/genpd/imx/imx93-pd.c

index 4b3300b090a8aa37805f4117416fe8e1625f6f24,0000000000000000000000000000000000000000..fbd3d92f8cd8f25b7980404dd450b15bfa8bf095
mode 100644,000000..100644
--- /dev/null
@@@ -1,1550 -1,0 +1,1550 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0+
 +/*
 + * Copyright 2017 Impinj, Inc
 + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
 + *
 + * Based on the code of analogus driver:
 + *
 + * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
 + */
 +
 +#include <linux/clk.h>
++#include <linux/of.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <linux/pm_runtime.h>
 +#include <linux/regmap.h>
 +#include <linux/regulator/consumer.h>
 +#include <linux/reset.h>
 +#include <linux/sizes.h>
 +#include <dt-bindings/power/imx7-power.h>
 +#include <dt-bindings/power/imx8mq-power.h>
 +#include <dt-bindings/power/imx8mm-power.h>
 +#include <dt-bindings/power/imx8mn-power.h>
 +#include <dt-bindings/power/imx8mp-power.h>
 +
 +#define GPC_LPCR_A_CORE_BSC                   0x000
 +
 +#define GPC_PGC_CPU_MAPPING           0x0ec
 +#define IMX8MP_GPC_PGC_CPU_MAPPING    0x1cc
 +
 +#define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN               BIT(6)
 +#define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN               BIT(5)
 +#define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN               BIT(4)
 +#define IMX7_PCIE_PHY_A_CORE_DOMAIN           BIT(3)
 +#define IMX7_MIPI_PHY_A_CORE_DOMAIN           BIT(2)
 +
 +#define IMX8M_PCIE2_A53_DOMAIN                        BIT(15)
 +#define IMX8M_MIPI_CSI2_A53_DOMAIN            BIT(14)
 +#define IMX8M_MIPI_CSI1_A53_DOMAIN            BIT(13)
 +#define IMX8M_DISP_A53_DOMAIN                 BIT(12)
 +#define IMX8M_HDMI_A53_DOMAIN                 BIT(11)
 +#define IMX8M_VPU_A53_DOMAIN                  BIT(10)
 +#define IMX8M_GPU_A53_DOMAIN                  BIT(9)
 +#define IMX8M_DDR2_A53_DOMAIN                 BIT(8)
 +#define IMX8M_DDR1_A53_DOMAIN                 BIT(7)
 +#define IMX8M_OTG2_A53_DOMAIN                 BIT(5)
 +#define IMX8M_OTG1_A53_DOMAIN                 BIT(4)
 +#define IMX8M_PCIE1_A53_DOMAIN                        BIT(3)
 +#define IMX8M_MIPI_A53_DOMAIN                 BIT(2)
 +
 +#define IMX8MM_VPUH1_A53_DOMAIN                       BIT(15)
 +#define IMX8MM_VPUG2_A53_DOMAIN                       BIT(14)
 +#define IMX8MM_VPUG1_A53_DOMAIN                       BIT(13)
 +#define IMX8MM_DISPMIX_A53_DOMAIN             BIT(12)
 +#define IMX8MM_VPUMIX_A53_DOMAIN              BIT(10)
 +#define IMX8MM_GPUMIX_A53_DOMAIN              BIT(9)
 +#define IMX8MM_GPU_A53_DOMAIN                 (BIT(8) | BIT(11))
 +#define IMX8MM_DDR1_A53_DOMAIN                        BIT(7)
 +#define IMX8MM_OTG2_A53_DOMAIN                        BIT(5)
 +#define IMX8MM_OTG1_A53_DOMAIN                        BIT(4)
 +#define IMX8MM_PCIE_A53_DOMAIN                        BIT(3)
 +#define IMX8MM_MIPI_A53_DOMAIN                        BIT(2)
 +
 +#define IMX8MN_DISPMIX_A53_DOMAIN             BIT(12)
 +#define IMX8MN_GPUMIX_A53_DOMAIN              BIT(9)
 +#define IMX8MN_DDR1_A53_DOMAIN                BIT(7)
 +#define IMX8MN_OTG1_A53_DOMAIN                BIT(4)
 +#define IMX8MN_MIPI_A53_DOMAIN                BIT(2)
 +
 +#define IMX8MP_MEDIA_ISPDWP_A53_DOMAIN        BIT(20)
 +#define IMX8MP_HSIOMIX_A53_DOMAIN             BIT(19)
 +#define IMX8MP_MIPI_PHY2_A53_DOMAIN           BIT(18)
 +#define IMX8MP_HDMI_PHY_A53_DOMAIN            BIT(17)
 +#define IMX8MP_HDMIMIX_A53_DOMAIN             BIT(16)
 +#define IMX8MP_VPU_VC8000E_A53_DOMAIN         BIT(15)
 +#define IMX8MP_VPU_G2_A53_DOMAIN              BIT(14)
 +#define IMX8MP_VPU_G1_A53_DOMAIN              BIT(13)
 +#define IMX8MP_MEDIAMIX_A53_DOMAIN            BIT(12)
 +#define IMX8MP_GPU3D_A53_DOMAIN                       BIT(11)
 +#define IMX8MP_VPUMIX_A53_DOMAIN              BIT(10)
 +#define IMX8MP_GPUMIX_A53_DOMAIN              BIT(9)
 +#define IMX8MP_GPU2D_A53_DOMAIN                       BIT(8)
 +#define IMX8MP_AUDIOMIX_A53_DOMAIN            BIT(7)
 +#define IMX8MP_MLMIX_A53_DOMAIN                       BIT(6)
 +#define IMX8MP_USB2_PHY_A53_DOMAIN            BIT(5)
 +#define IMX8MP_USB1_PHY_A53_DOMAIN            BIT(4)
 +#define IMX8MP_PCIE_PHY_A53_DOMAIN            BIT(3)
 +#define IMX8MP_MIPI_PHY1_A53_DOMAIN           BIT(2)
 +
 +#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ  0x0d8
 +#define IMX8MP_GPC_PU_PGC_SW_PDN_REQ  0x0e4
 +
 +#define GPC_PU_PGC_SW_PUP_REQ         0x0f8
 +#define GPC_PU_PGC_SW_PDN_REQ         0x104
 +
 +#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ          BIT(4)
 +#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ          BIT(3)
 +#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ          BIT(2)
 +#define IMX7_PCIE_PHY_SW_Pxx_REQ              BIT(1)
 +#define IMX7_MIPI_PHY_SW_Pxx_REQ              BIT(0)
 +
 +#define IMX8M_PCIE2_SW_Pxx_REQ                        BIT(13)
 +#define IMX8M_MIPI_CSI2_SW_Pxx_REQ            BIT(12)
 +#define IMX8M_MIPI_CSI1_SW_Pxx_REQ            BIT(11)
 +#define IMX8M_DISP_SW_Pxx_REQ                 BIT(10)
 +#define IMX8M_HDMI_SW_Pxx_REQ                 BIT(9)
 +#define IMX8M_VPU_SW_Pxx_REQ                  BIT(8)
 +#define IMX8M_GPU_SW_Pxx_REQ                  BIT(7)
 +#define IMX8M_DDR2_SW_Pxx_REQ                 BIT(6)
 +#define IMX8M_DDR1_SW_Pxx_REQ                 BIT(5)
 +#define IMX8M_OTG2_SW_Pxx_REQ                 BIT(3)
 +#define IMX8M_OTG1_SW_Pxx_REQ                 BIT(2)
 +#define IMX8M_PCIE1_SW_Pxx_REQ                        BIT(1)
 +#define IMX8M_MIPI_SW_Pxx_REQ                 BIT(0)
 +
 +#define IMX8MM_VPUH1_SW_Pxx_REQ                       BIT(13)
 +#define IMX8MM_VPUG2_SW_Pxx_REQ                       BIT(12)
 +#define IMX8MM_VPUG1_SW_Pxx_REQ                       BIT(11)
 +#define IMX8MM_DISPMIX_SW_Pxx_REQ             BIT(10)
 +#define IMX8MM_VPUMIX_SW_Pxx_REQ              BIT(8)
 +#define IMX8MM_GPUMIX_SW_Pxx_REQ              BIT(7)
 +#define IMX8MM_GPU_SW_Pxx_REQ                 (BIT(6) | BIT(9))
 +#define IMX8MM_DDR1_SW_Pxx_REQ                        BIT(5)
 +#define IMX8MM_OTG2_SW_Pxx_REQ                        BIT(3)
 +#define IMX8MM_OTG1_SW_Pxx_REQ                        BIT(2)
 +#define IMX8MM_PCIE_SW_Pxx_REQ                        BIT(1)
 +#define IMX8MM_MIPI_SW_Pxx_REQ                        BIT(0)
 +
 +#define IMX8MN_DISPMIX_SW_Pxx_REQ             BIT(10)
 +#define IMX8MN_GPUMIX_SW_Pxx_REQ              BIT(7)
 +#define IMX8MN_DDR1_SW_Pxx_REQ                BIT(5)
 +#define IMX8MN_OTG1_SW_Pxx_REQ                BIT(2)
 +#define IMX8MN_MIPI_SW_Pxx_REQ                BIT(0)
 +
 +#define IMX8MP_DDRMIX_Pxx_REQ                 BIT(19)
 +#define IMX8MP_MEDIA_ISP_DWP_Pxx_REQ          BIT(18)
 +#define IMX8MP_HSIOMIX_Pxx_REQ                        BIT(17)
 +#define IMX8MP_MIPI_PHY2_Pxx_REQ              BIT(16)
 +#define IMX8MP_HDMI_PHY_Pxx_REQ                       BIT(15)
 +#define IMX8MP_HDMIMIX_Pxx_REQ                        BIT(14)
 +#define IMX8MP_VPU_VC8K_Pxx_REQ                       BIT(13)
 +#define IMX8MP_VPU_G2_Pxx_REQ                 BIT(12)
 +#define IMX8MP_VPU_G1_Pxx_REQ                 BIT(11)
 +#define IMX8MP_MEDIMIX_Pxx_REQ                        BIT(10)
 +#define IMX8MP_GPU_3D_Pxx_REQ                 BIT(9)
 +#define IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ    BIT(8)
 +#define IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ                BIT(7)
 +#define IMX8MP_GPU_2D_Pxx_REQ                 BIT(6)
 +#define IMX8MP_AUDIOMIX_Pxx_REQ                       BIT(5)
 +#define IMX8MP_MLMIX_Pxx_REQ                  BIT(4)
 +#define IMX8MP_USB2_PHY_Pxx_REQ                       BIT(3)
 +#define IMX8MP_USB1_PHY_Pxx_REQ                       BIT(2)
 +#define IMX8MP_PCIE_PHY_SW_Pxx_REQ            BIT(1)
 +#define IMX8MP_MIPI_PHY1_SW_Pxx_REQ           BIT(0)
 +
 +#define GPC_M4_PU_PDN_FLG             0x1bc
 +
 +#define IMX8MP_GPC_PU_PWRHSK          0x190
 +#define GPC_PU_PWRHSK                 0x1fc
 +
 +#define IMX8M_GPU_HSK_PWRDNACKN                       BIT(26)
 +#define IMX8M_VPU_HSK_PWRDNACKN                       BIT(25)
 +#define IMX8M_DISP_HSK_PWRDNACKN              BIT(24)
 +#define IMX8M_GPU_HSK_PWRDNREQN                       BIT(6)
 +#define IMX8M_VPU_HSK_PWRDNREQN                       BIT(5)
 +#define IMX8M_DISP_HSK_PWRDNREQN              BIT(4)
 +
 +#define IMX8MM_GPUMIX_HSK_PWRDNACKN           BIT(29)
 +#define IMX8MM_GPU_HSK_PWRDNACKN              (BIT(27) | BIT(28))
 +#define IMX8MM_VPUMIX_HSK_PWRDNACKN           BIT(26)
 +#define IMX8MM_DISPMIX_HSK_PWRDNACKN          BIT(25)
 +#define IMX8MM_HSIO_HSK_PWRDNACKN             (BIT(23) | BIT(24))
 +#define IMX8MM_GPUMIX_HSK_PWRDNREQN           BIT(11)
 +#define IMX8MM_GPU_HSK_PWRDNREQN              (BIT(9) | BIT(10))
 +#define IMX8MM_VPUMIX_HSK_PWRDNREQN           BIT(8)
 +#define IMX8MM_DISPMIX_HSK_PWRDNREQN          BIT(7)
 +#define IMX8MM_HSIO_HSK_PWRDNREQN             (BIT(5) | BIT(6))
 +
 +#define IMX8MN_GPUMIX_HSK_PWRDNACKN           (BIT(29) | BIT(27))
 +#define IMX8MN_DISPMIX_HSK_PWRDNACKN          BIT(25)
 +#define IMX8MN_HSIO_HSK_PWRDNACKN             BIT(23)
 +#define IMX8MN_GPUMIX_HSK_PWRDNREQN           (BIT(11) | BIT(9))
 +#define IMX8MN_DISPMIX_HSK_PWRDNREQN          BIT(7)
 +#define IMX8MN_HSIO_HSK_PWRDNREQN             BIT(5)
 +
 +#define IMX8MP_MEDIAMIX_PWRDNACKN             BIT(30)
 +#define IMX8MP_HDMIMIX_PWRDNACKN              BIT(29)
 +#define IMX8MP_HSIOMIX_PWRDNACKN              BIT(28)
 +#define IMX8MP_VPUMIX_PWRDNACKN                       BIT(26)
 +#define IMX8MP_GPUMIX_PWRDNACKN                       BIT(25)
 +#define IMX8MP_MLMIX_PWRDNACKN                        (BIT(23) | BIT(24))
 +#define IMX8MP_AUDIOMIX_PWRDNACKN             (BIT(20) | BIT(31))
 +#define IMX8MP_MEDIAMIX_PWRDNREQN             BIT(14)
 +#define IMX8MP_HDMIMIX_PWRDNREQN              BIT(13)
 +#define IMX8MP_HSIOMIX_PWRDNREQN              BIT(12)
 +#define IMX8MP_VPUMIX_PWRDNREQN                       BIT(10)
 +#define IMX8MP_GPUMIX_PWRDNREQN                       BIT(9)
 +#define IMX8MP_MLMIX_PWRDNREQN                        (BIT(7) | BIT(8))
 +#define IMX8MP_AUDIOMIX_PWRDNREQN             (BIT(4) | BIT(15))
 +
 +/*
 + * The PGC offset values in Reference Manual
 + * (Rev. 1, 01/2018 and the older ones) GPC chapter's
 + * GPC_PGC memory map are incorrect, below offset
 + * values are from design RTL.
 + */
 +#define IMX7_PGC_MIPI                 16
 +#define IMX7_PGC_PCIE                 17
 +#define IMX7_PGC_USB_HSIC             20
 +
 +#define IMX8M_PGC_MIPI                        16
 +#define IMX8M_PGC_PCIE1                       17
 +#define IMX8M_PGC_OTG1                        18
 +#define IMX8M_PGC_OTG2                        19
 +#define IMX8M_PGC_DDR1                        21
 +#define IMX8M_PGC_GPU                 23
 +#define IMX8M_PGC_VPU                 24
 +#define IMX8M_PGC_DISP                        26
 +#define IMX8M_PGC_MIPI_CSI1           27
 +#define IMX8M_PGC_MIPI_CSI2           28
 +#define IMX8M_PGC_PCIE2                       29
 +
 +#define IMX8MM_PGC_MIPI                       16
 +#define IMX8MM_PGC_PCIE                       17
 +#define IMX8MM_PGC_OTG1                       18
 +#define IMX8MM_PGC_OTG2                       19
 +#define IMX8MM_PGC_DDR1                       21
 +#define IMX8MM_PGC_GPU2D              22
 +#define IMX8MM_PGC_GPUMIX             23
 +#define IMX8MM_PGC_VPUMIX             24
 +#define IMX8MM_PGC_GPU3D              25
 +#define IMX8MM_PGC_DISPMIX            26
 +#define IMX8MM_PGC_VPUG1              27
 +#define IMX8MM_PGC_VPUG2              28
 +#define IMX8MM_PGC_VPUH1              29
 +
 +#define IMX8MN_PGC_MIPI               16
 +#define IMX8MN_PGC_OTG1               18
 +#define IMX8MN_PGC_DDR1               21
 +#define IMX8MN_PGC_GPUMIX             23
 +#define IMX8MN_PGC_DISPMIX            26
 +
 +#define IMX8MP_PGC_NOC                        9
 +#define IMX8MP_PGC_MIPI1              12
 +#define IMX8MP_PGC_PCIE                       13
 +#define IMX8MP_PGC_USB1                       14
 +#define IMX8MP_PGC_USB2                       15
 +#define IMX8MP_PGC_MLMIX              16
 +#define IMX8MP_PGC_AUDIOMIX           17
 +#define IMX8MP_PGC_GPU2D              18
 +#define IMX8MP_PGC_GPUMIX             19
 +#define IMX8MP_PGC_VPUMIX             20
 +#define IMX8MP_PGC_GPU3D              21
 +#define IMX8MP_PGC_MEDIAMIX           22
 +#define IMX8MP_PGC_VPU_G1             23
 +#define IMX8MP_PGC_VPU_G2             24
 +#define IMX8MP_PGC_VPU_VC8000E                25
 +#define IMX8MP_PGC_HDMIMIX            26
 +#define IMX8MP_PGC_HDMI                       27
 +#define IMX8MP_PGC_MIPI2              28
 +#define IMX8MP_PGC_HSIOMIX            29
 +#define IMX8MP_PGC_MEDIA_ISP_DWP      30
 +#define IMX8MP_PGC_DDRMIX             31
 +
 +#define GPC_PGC_CTRL(n)                       (0x800 + (n) * 0x40)
 +#define GPC_PGC_SR(n)                 (GPC_PGC_CTRL(n) + 0xc)
 +
 +#define GPC_PGC_CTRL_PCR              BIT(0)
 +
 +struct imx_pgc_regs {
 +      u16 map;
 +      u16 pup;
 +      u16 pdn;
 +      u16 hsk;
 +};
 +
 +struct imx_pgc_domain {
 +      struct generic_pm_domain genpd;
 +      struct regmap *regmap;
 +      const struct imx_pgc_regs *regs;
 +      struct regulator *regulator;
 +      struct reset_control *reset;
 +      struct clk_bulk_data *clks;
 +      int num_clks;
 +
 +      unsigned long pgc;
 +
 +      const struct {
 +              u32 pxx;
 +              u32 map;
 +              u32 hskreq;
 +              u32 hskack;
 +      } bits;
 +
 +      const int voltage;
 +      const bool keep_clocks;
 +      struct device *dev;
 +
 +      unsigned int pgc_sw_pup_reg;
 +      unsigned int pgc_sw_pdn_reg;
 +};
 +
 +struct imx_pgc_domain_data {
 +      const struct imx_pgc_domain *domains;
 +      size_t domains_num;
 +      const struct regmap_access_table *reg_access_table;
 +      const struct imx_pgc_regs *pgc_regs;
 +};
 +
 +static inline struct imx_pgc_domain *
 +to_imx_pgc_domain(struct generic_pm_domain *genpd)
 +{
 +      return container_of(genpd, struct imx_pgc_domain, genpd);
 +}
 +
 +static int imx_pgc_power_up(struct generic_pm_domain *genpd)
 +{
 +      struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
 +      u32 reg_val, pgc;
 +      int ret;
 +
 +      ret = pm_runtime_get_sync(domain->dev);
 +      if (ret < 0) {
 +              pm_runtime_put_noidle(domain->dev);
 +              return ret;
 +      }
 +
 +      if (!IS_ERR(domain->regulator)) {
 +              ret = regulator_enable(domain->regulator);
 +              if (ret) {
 +                      dev_err(domain->dev,
 +                              "failed to enable regulator: %pe\n",
 +                              ERR_PTR(ret));
 +                      goto out_put_pm;
 +              }
 +      }
 +
 +      reset_control_assert(domain->reset);
 +
 +      /* Enable reset clocks for all devices in the domain */
 +      ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
 +      if (ret) {
 +              dev_err(domain->dev, "failed to enable reset clocks\n");
 +              goto out_regulator_disable;
 +      }
 +
 +      /* delays for reset to propagate */
 +      udelay(5);
 +
 +      if (domain->bits.pxx) {
 +              /* request the domain to power up */
 +              regmap_update_bits(domain->regmap, domain->regs->pup,
 +                                 domain->bits.pxx, domain->bits.pxx);
 +              /*
 +               * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
 +               * for PUP_REQ/PDN_REQ bit to be cleared
 +               */
 +              ret = regmap_read_poll_timeout(domain->regmap,
 +                                             domain->regs->pup, reg_val,
 +                                             !(reg_val & domain->bits.pxx),
 +                                             0, USEC_PER_MSEC);
 +              if (ret) {
 +                      dev_err(domain->dev, "failed to command PGC\n");
 +                      goto out_clk_disable;
 +              }
 +
 +              /* disable power control */
 +              for_each_set_bit(pgc, &domain->pgc, 32) {
 +                      regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc),
 +                                        GPC_PGC_CTRL_PCR);
 +              }
 +      }
 +
 +      /* delay for reset to propagate */
 +      udelay(5);
 +
 +      reset_control_deassert(domain->reset);
 +
 +      /* request the ADB400 to power up */
 +      if (domain->bits.hskreq) {
 +              regmap_update_bits(domain->regmap, domain->regs->hsk,
 +                                 domain->bits.hskreq, domain->bits.hskreq);
 +
 +              /*
 +               * ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, reg_val,
 +               *                                (reg_val & domain->bits.hskack), 0,
 +               *                                USEC_PER_MSEC);
 +               * Technically we need the commented code to wait handshake. But that needs
 +               * the BLK-CTL module BUS clk-en bit being set.
 +               *
 +               * There is a separate BLK-CTL module and we will have such a driver for it,
 +               * that driver will set the BUS clk-en bit and handshake will be triggered
 +               * automatically there. Just add a delay and suppose the handshake finish
 +               * after that.
 +               */
 +      }
 +
 +      /* Disable reset clocks for all devices in the domain */
 +      if (!domain->keep_clocks)
 +              clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +
 +      return 0;
 +
 +out_clk_disable:
 +      clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +out_regulator_disable:
 +      if (!IS_ERR(domain->regulator))
 +              regulator_disable(domain->regulator);
 +out_put_pm:
 +      pm_runtime_put(domain->dev);
 +
 +      return ret;
 +}
 +
 +static int imx_pgc_power_down(struct generic_pm_domain *genpd)
 +{
 +      struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
 +      u32 reg_val, pgc;
 +      int ret;
 +
 +      /* Enable reset clocks for all devices in the domain */
 +      if (!domain->keep_clocks) {
 +              ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
 +              if (ret) {
 +                      dev_err(domain->dev, "failed to enable reset clocks\n");
 +                      return ret;
 +              }
 +      }
 +
 +      /* request the ADB400 to power down */
 +      if (domain->bits.hskreq) {
 +              regmap_clear_bits(domain->regmap, domain->regs->hsk,
 +                                domain->bits.hskreq);
 +
 +              ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk,
 +                                             reg_val,
 +                                             !(reg_val & domain->bits.hskack),
 +                                             0, USEC_PER_MSEC);
 +              if (ret) {
 +                      dev_err(domain->dev, "failed to power down ADB400\n");
 +                      goto out_clk_disable;
 +              }
 +      }
 +
 +      if (domain->bits.pxx) {
 +              /* enable power control */
 +              for_each_set_bit(pgc, &domain->pgc, 32) {
 +                      regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc),
 +                                         GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
 +              }
 +
 +              /* request the domain to power down */
 +              regmap_update_bits(domain->regmap, domain->regs->pdn,
 +                                 domain->bits.pxx, domain->bits.pxx);
 +              /*
 +               * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
 +               * for PUP_REQ/PDN_REQ bit to be cleared
 +               */
 +              ret = regmap_read_poll_timeout(domain->regmap,
 +                                             domain->regs->pdn, reg_val,
 +                                             !(reg_val & domain->bits.pxx),
 +                                             0, USEC_PER_MSEC);
 +              if (ret) {
 +                      dev_err(domain->dev, "failed to command PGC\n");
 +                      goto out_clk_disable;
 +              }
 +      }
 +
 +      /* Disable reset clocks for all devices in the domain */
 +      clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +
 +      if (!IS_ERR(domain->regulator)) {
 +              ret = regulator_disable(domain->regulator);
 +              if (ret) {
 +                      dev_err(domain->dev,
 +                              "failed to disable regulator: %pe\n",
 +                              ERR_PTR(ret));
 +                      return ret;
 +              }
 +      }
 +
 +      pm_runtime_put_sync_suspend(domain->dev);
 +
 +      return 0;
 +
 +out_clk_disable:
 +      if (!domain->keep_clocks)
 +              clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +
 +      return ret;
 +}
 +
 +static const struct imx_pgc_domain imx7_pgc_domains[] = {
 +      [IMX7_POWER_DOMAIN_MIPI_PHY] = {
 +              .genpd = {
 +                      .name      = "mipi-phy",
 +              },
 +              .bits  = {
 +                      .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ,
 +                      .map = IMX7_MIPI_PHY_A_CORE_DOMAIN,
 +              },
 +              .voltage   = 1000000,
 +              .pgc       = BIT(IMX7_PGC_MIPI),
 +      },
 +
 +      [IMX7_POWER_DOMAIN_PCIE_PHY] = {
 +              .genpd = {
 +                      .name      = "pcie-phy",
 +              },
 +              .bits  = {
 +                      .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ,
 +                      .map = IMX7_PCIE_PHY_A_CORE_DOMAIN,
 +              },
 +              .voltage   = 1000000,
 +              .pgc       = BIT(IMX7_PGC_PCIE),
 +      },
 +
 +      [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
 +              .genpd = {
 +                      .name      = "usb-hsic-phy",
 +              },
 +              .bits  = {
 +                      .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ,
 +                      .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN,
 +              },
 +              .voltage   = 1200000,
 +              .pgc       = BIT(IMX7_PGC_USB_HSIC),
 +      },
 +};
 +
 +static const struct regmap_range imx7_yes_ranges[] = {
 +              regmap_reg_range(GPC_LPCR_A_CORE_BSC,
 +                               GPC_M4_PU_PDN_FLG),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_MIPI),
 +                               GPC_PGC_SR(IMX7_PGC_MIPI)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_PCIE),
 +                               GPC_PGC_SR(IMX7_PGC_PCIE)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_USB_HSIC),
 +                               GPC_PGC_SR(IMX7_PGC_USB_HSIC)),
 +};
 +
 +static const struct regmap_access_table imx7_access_table = {
 +      .yes_ranges     = imx7_yes_ranges,
 +      .n_yes_ranges   = ARRAY_SIZE(imx7_yes_ranges),
 +};
 +
 +static const struct imx_pgc_regs imx7_pgc_regs = {
 +      .map = GPC_PGC_CPU_MAPPING,
 +      .pup = GPC_PU_PGC_SW_PUP_REQ,
 +      .pdn = GPC_PU_PGC_SW_PDN_REQ,
 +      .hsk = GPC_PU_PWRHSK,
 +};
 +
 +static const struct imx_pgc_domain_data imx7_pgc_domain_data = {
 +      .domains = imx7_pgc_domains,
 +      .domains_num = ARRAY_SIZE(imx7_pgc_domains),
 +      .reg_access_table = &imx7_access_table,
 +      .pgc_regs = &imx7_pgc_regs,
 +};
 +
 +static const struct imx_pgc_domain imx8m_pgc_domains[] = {
 +      [IMX8M_POWER_DOMAIN_MIPI] = {
 +              .genpd = {
 +                      .name      = "mipi",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_MIPI_SW_Pxx_REQ,
 +                      .map = IMX8M_MIPI_A53_DOMAIN,
 +              },
 +              .pgc       = BIT(IMX8M_PGC_MIPI),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_PCIE1] = {
 +              .genpd = {
 +                      .name = "pcie1",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_PCIE1_SW_Pxx_REQ,
 +                      .map = IMX8M_PCIE1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_PCIE1),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_USB_OTG1] = {
 +              .genpd = {
 +                      .name = "usb-otg1",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_OTG1_SW_Pxx_REQ,
 +                      .map = IMX8M_OTG1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_OTG1),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_USB_OTG2] = {
 +              .genpd = {
 +                      .name = "usb-otg2",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_OTG2_SW_Pxx_REQ,
 +                      .map = IMX8M_OTG2_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_OTG2),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_DDR1] = {
 +              .genpd = {
 +                      .name = "ddr1",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_DDR1_SW_Pxx_REQ,
 +                      .map = IMX8M_DDR2_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_DDR1),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_GPU] = {
 +              .genpd = {
 +                      .name = "gpu",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_GPU_SW_Pxx_REQ,
 +                      .map = IMX8M_GPU_A53_DOMAIN,
 +                      .hskreq = IMX8M_GPU_HSK_PWRDNREQN,
 +                      .hskack = IMX8M_GPU_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_GPU),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_VPU] = {
 +              .genpd = {
 +                      .name = "vpu",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_VPU_SW_Pxx_REQ,
 +                      .map = IMX8M_VPU_A53_DOMAIN,
 +                      .hskreq = IMX8M_VPU_HSK_PWRDNREQN,
 +                      .hskack = IMX8M_VPU_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_VPU),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_DISP] = {
 +              .genpd = {
 +                      .name = "disp",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_DISP_SW_Pxx_REQ,
 +                      .map = IMX8M_DISP_A53_DOMAIN,
 +                      .hskreq = IMX8M_DISP_HSK_PWRDNREQN,
 +                      .hskack = IMX8M_DISP_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_DISP),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_MIPI_CSI1] = {
 +              .genpd = {
 +                      .name = "mipi-csi1",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ,
 +                      .map = IMX8M_MIPI_CSI1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_MIPI_CSI1),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_MIPI_CSI2] = {
 +              .genpd = {
 +                      .name = "mipi-csi2",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ,
 +                      .map = IMX8M_MIPI_CSI2_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_MIPI_CSI2),
 +      },
 +
 +      [IMX8M_POWER_DOMAIN_PCIE2] = {
 +              .genpd = {
 +                      .name = "pcie2",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8M_PCIE2_SW_Pxx_REQ,
 +                      .map = IMX8M_PCIE2_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8M_PGC_PCIE2),
 +      },
 +};
 +
 +static const struct regmap_range imx8m_yes_ranges[] = {
 +              regmap_reg_range(GPC_LPCR_A_CORE_BSC,
 +                               GPC_PU_PWRHSK),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI),
 +                               GPC_PGC_SR(IMX8M_PGC_MIPI)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1),
 +                               GPC_PGC_SR(IMX8M_PGC_PCIE1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG1),
 +                               GPC_PGC_SR(IMX8M_PGC_OTG1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG2),
 +                               GPC_PGC_SR(IMX8M_PGC_OTG2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DDR1),
 +                               GPC_PGC_SR(IMX8M_PGC_DDR1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_GPU),
 +                               GPC_PGC_SR(IMX8M_PGC_GPU)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_VPU),
 +                               GPC_PGC_SR(IMX8M_PGC_VPU)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DISP),
 +                               GPC_PGC_SR(IMX8M_PGC_DISP)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI1),
 +                               GPC_PGC_SR(IMX8M_PGC_MIPI_CSI1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI2),
 +                               GPC_PGC_SR(IMX8M_PGC_MIPI_CSI2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE2),
 +                               GPC_PGC_SR(IMX8M_PGC_PCIE2)),
 +};
 +
 +static const struct regmap_access_table imx8m_access_table = {
 +      .yes_ranges     = imx8m_yes_ranges,
 +      .n_yes_ranges   = ARRAY_SIZE(imx8m_yes_ranges),
 +};
 +
 +static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
 +      .domains = imx8m_pgc_domains,
 +      .domains_num = ARRAY_SIZE(imx8m_pgc_domains),
 +      .reg_access_table = &imx8m_access_table,
 +      .pgc_regs = &imx7_pgc_regs,
 +};
 +
 +static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
 +      [IMX8MM_POWER_DOMAIN_HSIOMIX] = {
 +              .genpd = {
 +                      .name = "hsiomix",
 +              },
 +              .bits  = {
 +                      .pxx = 0, /* no power sequence control */
 +                      .map = 0, /* no power sequence control */
 +                      .hskreq = IMX8MM_HSIO_HSK_PWRDNREQN,
 +                      .hskack = IMX8MM_HSIO_HSK_PWRDNACKN,
 +              },
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_PCIE] = {
 +              .genpd = {
 +                      .name = "pcie",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_PCIE_SW_Pxx_REQ,
 +                      .map = IMX8MM_PCIE_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_PCIE),
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_OTG1] = {
 +              .genpd = {
 +                      .name = "usb-otg1",
 +                      .flags = GENPD_FLAG_ACTIVE_WAKEUP,
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_OTG1_SW_Pxx_REQ,
 +                      .map = IMX8MM_OTG1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_OTG1),
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_OTG2] = {
 +              .genpd = {
 +                      .name = "usb-otg2",
 +                      .flags = GENPD_FLAG_ACTIVE_WAKEUP,
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_OTG2_SW_Pxx_REQ,
 +                      .map = IMX8MM_OTG2_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_OTG2),
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_GPUMIX] = {
 +              .genpd = {
 +                      .name = "gpumix",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_GPUMIX_SW_Pxx_REQ,
 +                      .map = IMX8MM_GPUMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN,
 +                      .hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_GPUMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_GPU] = {
 +              .genpd = {
 +                      .name = "gpu",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_GPU_SW_Pxx_REQ,
 +                      .map = IMX8MM_GPU_A53_DOMAIN,
 +                      .hskreq = IMX8MM_GPU_HSK_PWRDNREQN,
 +                      .hskack = IMX8MM_GPU_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D),
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_VPUMIX] = {
 +              .genpd = {
 +                      .name = "vpumix",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_VPUMIX_SW_Pxx_REQ,
 +                      .map = IMX8MM_VPUMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN,
 +                      .hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_VPUMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_VPUG1] = {
 +              .genpd = {
 +                      .name = "vpu-g1",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_VPUG1_SW_Pxx_REQ,
 +                      .map = IMX8MM_VPUG1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_VPUG1),
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_VPUG2] = {
 +              .genpd = {
 +                      .name = "vpu-g2",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_VPUG2_SW_Pxx_REQ,
 +                      .map = IMX8MM_VPUG2_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_VPUG2),
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_VPUH1] = {
 +              .genpd = {
 +                      .name = "vpu-h1",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_VPUH1_SW_Pxx_REQ,
 +                      .map = IMX8MM_VPUH1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_VPUH1),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_DISPMIX] = {
 +              .genpd = {
 +                      .name = "dispmix",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_DISPMIX_SW_Pxx_REQ,
 +                      .map = IMX8MM_DISPMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN,
 +                      .hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_DISPMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MM_POWER_DOMAIN_MIPI] = {
 +              .genpd = {
 +                      .name = "mipi",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MM_MIPI_SW_Pxx_REQ,
 +                      .map = IMX8MM_MIPI_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MM_PGC_MIPI),
 +      },
 +};
 +
 +static const struct regmap_range imx8mm_yes_ranges[] = {
 +              regmap_reg_range(GPC_LPCR_A_CORE_BSC,
 +                               GPC_PU_PWRHSK),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_MIPI),
 +                               GPC_PGC_SR(IMX8MM_PGC_MIPI)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_PCIE),
 +                               GPC_PGC_SR(IMX8MM_PGC_PCIE)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_OTG1),
 +                               GPC_PGC_SR(IMX8MM_PGC_OTG1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_OTG2),
 +                               GPC_PGC_SR(IMX8MM_PGC_OTG2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_DDR1),
 +                               GPC_PGC_SR(IMX8MM_PGC_DDR1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPU2D),
 +                               GPC_PGC_SR(IMX8MM_PGC_GPU2D)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPUMIX),
 +                               GPC_PGC_SR(IMX8MM_PGC_GPUMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUMIX),
 +                               GPC_PGC_SR(IMX8MM_PGC_VPUMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPU3D),
 +                               GPC_PGC_SR(IMX8MM_PGC_GPU3D)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_DISPMIX),
 +                               GPC_PGC_SR(IMX8MM_PGC_DISPMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUG1),
 +                               GPC_PGC_SR(IMX8MM_PGC_VPUG1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUG2),
 +                               GPC_PGC_SR(IMX8MM_PGC_VPUG2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUH1),
 +                               GPC_PGC_SR(IMX8MM_PGC_VPUH1)),
 +};
 +
 +static const struct regmap_access_table imx8mm_access_table = {
 +      .yes_ranges     = imx8mm_yes_ranges,
 +      .n_yes_ranges   = ARRAY_SIZE(imx8mm_yes_ranges),
 +};
 +
 +static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = {
 +      .domains = imx8mm_pgc_domains,
 +      .domains_num = ARRAY_SIZE(imx8mm_pgc_domains),
 +      .reg_access_table = &imx8mm_access_table,
 +      .pgc_regs = &imx7_pgc_regs,
 +};
 +
 +static const struct imx_pgc_domain imx8mp_pgc_domains[] = {
 +      [IMX8MP_POWER_DOMAIN_MIPI_PHY1] = {
 +              .genpd = {
 +                      .name = "mipi-phy1",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ,
 +                      .map = IMX8MP_MIPI_PHY1_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_MIPI1),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_PCIE_PHY] = {
 +              .genpd = {
 +                      .name = "pcie-phy1",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ,
 +                      .map = IMX8MP_PCIE_PHY_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_PCIE),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_USB1_PHY] = {
 +              .genpd = {
 +                      .name = "usb-otg1",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_USB1_PHY_Pxx_REQ,
 +                      .map = IMX8MP_USB1_PHY_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_USB1),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_USB2_PHY] = {
 +              .genpd = {
 +                      .name = "usb-otg2",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_USB2_PHY_Pxx_REQ,
 +                      .map = IMX8MP_USB2_PHY_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_USB2),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_MLMIX] = {
 +              .genpd = {
 +                      .name = "mlmix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_MLMIX_Pxx_REQ,
 +                      .map = IMX8MP_MLMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_MLMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_MLMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_MLMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_AUDIOMIX] = {
 +              .genpd = {
 +                      .name = "audiomix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_AUDIOMIX_Pxx_REQ,
 +                      .map = IMX8MP_AUDIOMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_AUDIOMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_AUDIOMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_AUDIOMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_GPU2D] = {
 +              .genpd = {
 +                      .name = "gpu2d",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_GPU_2D_Pxx_REQ,
 +                      .map = IMX8MP_GPU2D_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_GPU2D),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_GPUMIX] = {
 +              .genpd = {
 +                      .name = "gpumix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ,
 +                      .map = IMX8MP_GPUMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_GPUMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_GPUMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_GPUMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_VPUMIX] = {
 +              .genpd = {
 +                      .name = "vpumix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ,
 +                      .map = IMX8MP_VPUMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_VPUMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_VPUMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_VPUMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_GPU3D] = {
 +              .genpd = {
 +                      .name = "gpu3d",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_GPU_3D_Pxx_REQ,
 +                      .map = IMX8MP_GPU3D_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_GPU3D),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_MEDIAMIX] = {
 +              .genpd = {
 +                      .name = "mediamix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_MEDIMIX_Pxx_REQ,
 +                      .map = IMX8MP_MEDIAMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_MEDIAMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_MEDIAMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_VPU_G1] = {
 +              .genpd = {
 +                      .name = "vpu-g1",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_VPU_G1_Pxx_REQ,
 +                      .map = IMX8MP_VPU_G1_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_VPU_G1),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_VPU_G2] = {
 +              .genpd = {
 +                      .name = "vpu-g2",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_VPU_G2_Pxx_REQ,
 +                      .map = IMX8MP_VPU_G2_A53_DOMAIN
 +              },
 +              .pgc = BIT(IMX8MP_PGC_VPU_G2),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_VPU_VC8000E] = {
 +              .genpd = {
 +                      .name = "vpu-h1",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_VPU_VC8K_Pxx_REQ,
 +                      .map = IMX8MP_VPU_VC8000E_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_VPU_VC8000E),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_HDMIMIX] = {
 +              .genpd = {
 +                      .name = "hdmimix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_HDMIMIX_Pxx_REQ,
 +                      .map = IMX8MP_HDMIMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_HDMIMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_HDMIMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_HDMIMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_HDMI_PHY] = {
 +              .genpd = {
 +                      .name = "hdmi-phy",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_HDMI_PHY_Pxx_REQ,
 +                      .map = IMX8MP_HDMI_PHY_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_HDMI),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_MIPI_PHY2] = {
 +              .genpd = {
 +                      .name = "mipi-phy2",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_MIPI_PHY2_Pxx_REQ,
 +                      .map = IMX8MP_MIPI_PHY2_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_MIPI2),
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_HSIOMIX] = {
 +              .genpd = {
 +                      .name = "hsiomix",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_HSIOMIX_Pxx_REQ,
 +                      .map = IMX8MP_HSIOMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MP_HSIOMIX_PWRDNREQN,
 +                      .hskack = IMX8MP_HSIOMIX_PWRDNACKN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_HSIOMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MP_POWER_DOMAIN_MEDIAMIX_ISPDWP] = {
 +              .genpd = {
 +                      .name = "mediamix-isp-dwp",
 +              },
 +              .bits = {
 +                      .pxx = IMX8MP_MEDIA_ISP_DWP_Pxx_REQ,
 +                      .map = IMX8MP_MEDIA_ISPDWP_A53_DOMAIN,
 +              },
 +              .pgc = BIT(IMX8MP_PGC_MEDIA_ISP_DWP),
 +      },
 +};
 +
 +static const struct regmap_range imx8mp_yes_ranges[] = {
 +              regmap_reg_range(GPC_LPCR_A_CORE_BSC,
 +                               IMX8MP_GPC_PGC_CPU_MAPPING),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_NOC),
 +                               GPC_PGC_SR(IMX8MP_PGC_NOC)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI1),
 +                               GPC_PGC_SR(IMX8MP_PGC_MIPI1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_PCIE),
 +                               GPC_PGC_SR(IMX8MP_PGC_PCIE)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB1),
 +                               GPC_PGC_SR(IMX8MP_PGC_USB1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB2),
 +                               GPC_PGC_SR(IMX8MP_PGC_USB2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MLMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_MLMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_AUDIOMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_AUDIOMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU2D),
 +                               GPC_PGC_SR(IMX8MP_PGC_GPU2D)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPUMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_GPUMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPUMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_VPUMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU3D),
 +                               GPC_PGC_SR(IMX8MP_PGC_GPU3D)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIAMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_MEDIAMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G1),
 +                               GPC_PGC_SR(IMX8MP_PGC_VPU_G1)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G2),
 +                               GPC_PGC_SR(IMX8MP_PGC_VPU_G2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_VC8000E),
 +                               GPC_PGC_SR(IMX8MP_PGC_VPU_VC8000E)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMIMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_HDMIMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMI),
 +                               GPC_PGC_SR(IMX8MP_PGC_HDMI)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI2),
 +                               GPC_PGC_SR(IMX8MP_PGC_MIPI2)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HSIOMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_HSIOMIX)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIA_ISP_DWP),
 +                               GPC_PGC_SR(IMX8MP_PGC_MEDIA_ISP_DWP)),
 +              regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_DDRMIX),
 +                               GPC_PGC_SR(IMX8MP_PGC_DDRMIX)),
 +};
 +
 +static const struct regmap_access_table imx8mp_access_table = {
 +      .yes_ranges     = imx8mp_yes_ranges,
 +      .n_yes_ranges   = ARRAY_SIZE(imx8mp_yes_ranges),
 +};
 +
 +static const struct imx_pgc_regs imx8mp_pgc_regs = {
 +      .map = IMX8MP_GPC_PGC_CPU_MAPPING,
 +      .pup = IMX8MP_GPC_PU_PGC_SW_PUP_REQ,
 +      .pdn = IMX8MP_GPC_PU_PGC_SW_PDN_REQ,
 +      .hsk = IMX8MP_GPC_PU_PWRHSK,
 +};
 +static const struct imx_pgc_domain_data imx8mp_pgc_domain_data = {
 +      .domains = imx8mp_pgc_domains,
 +      .domains_num = ARRAY_SIZE(imx8mp_pgc_domains),
 +      .reg_access_table = &imx8mp_access_table,
 +      .pgc_regs = &imx8mp_pgc_regs,
 +};
 +
 +static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
 +      [IMX8MN_POWER_DOMAIN_HSIOMIX] = {
 +              .genpd = {
 +                      .name = "hsiomix",
 +              },
 +              .bits  = {
 +                      .pxx = 0, /* no power sequence control */
 +                      .map = 0, /* no power sequence control */
 +                      .hskreq = IMX8MN_HSIO_HSK_PWRDNREQN,
 +                      .hskack = IMX8MN_HSIO_HSK_PWRDNACKN,
 +              },
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MN_POWER_DOMAIN_OTG1] = {
 +              .genpd = {
 +                      .name = "usb-otg1",
 +                      .flags = GENPD_FLAG_ACTIVE_WAKEUP,
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MN_OTG1_SW_Pxx_REQ,
 +                      .map = IMX8MN_OTG1_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MN_PGC_OTG1),
 +      },
 +
 +      [IMX8MN_POWER_DOMAIN_GPUMIX] = {
 +              .genpd = {
 +                      .name = "gpumix",
 +              },
 +              .bits  = {
 +                      .pxx = IMX8MN_GPUMIX_SW_Pxx_REQ,
 +                      .map = IMX8MN_GPUMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN,
 +                      .hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8MN_PGC_GPUMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MN_POWER_DOMAIN_DISPMIX] = {
 +              .genpd = {
 +                      .name = "dispmix",
 +              },
 +                      .bits  = {
 +                      .pxx = IMX8MN_DISPMIX_SW_Pxx_REQ,
 +                      .map = IMX8MN_DISPMIX_A53_DOMAIN,
 +                      .hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN,
 +                      .hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN,
 +              },
 +              .pgc   = BIT(IMX8MN_PGC_DISPMIX),
 +              .keep_clocks = true,
 +      },
 +
 +      [IMX8MN_POWER_DOMAIN_MIPI] = {
 +              .genpd = {
 +                      .name = "mipi",
 +              },
 +                      .bits  = {
 +                      .pxx = IMX8MN_MIPI_SW_Pxx_REQ,
 +                      .map = IMX8MN_MIPI_A53_DOMAIN,
 +              },
 +              .pgc   = BIT(IMX8MN_PGC_MIPI),
 +      },
 +};
 +
 +static const struct regmap_range imx8mn_yes_ranges[] = {
 +      regmap_reg_range(GPC_LPCR_A_CORE_BSC,
 +                       GPC_PU_PWRHSK),
 +      regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_MIPI),
 +                       GPC_PGC_SR(IMX8MN_PGC_MIPI)),
 +      regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_OTG1),
 +                       GPC_PGC_SR(IMX8MN_PGC_OTG1)),
 +      regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_DDR1),
 +                       GPC_PGC_SR(IMX8MN_PGC_DDR1)),
 +      regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_GPUMIX),
 +                       GPC_PGC_SR(IMX8MN_PGC_GPUMIX)),
 +      regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_DISPMIX),
 +                       GPC_PGC_SR(IMX8MN_PGC_DISPMIX)),
 +};
 +
 +static const struct regmap_access_table imx8mn_access_table = {
 +      .yes_ranges     = imx8mn_yes_ranges,
 +      .n_yes_ranges   = ARRAY_SIZE(imx8mn_yes_ranges),
 +};
 +
 +static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = {
 +      .domains = imx8mn_pgc_domains,
 +      .domains_num = ARRAY_SIZE(imx8mn_pgc_domains),
 +      .reg_access_table = &imx8mn_access_table,
 +      .pgc_regs = &imx7_pgc_regs,
 +};
 +
 +static int imx_pgc_domain_probe(struct platform_device *pdev)
 +{
 +      struct imx_pgc_domain *domain = pdev->dev.platform_data;
 +      int ret;
 +
 +      domain->dev = &pdev->dev;
 +
 +      domain->regulator = devm_regulator_get_optional(domain->dev, "power");
 +      if (IS_ERR(domain->regulator)) {
 +              if (PTR_ERR(domain->regulator) != -ENODEV)
 +                      return dev_err_probe(domain->dev, PTR_ERR(domain->regulator),
 +                                           "Failed to get domain's regulator\n");
 +      } else if (domain->voltage) {
 +              regulator_set_voltage(domain->regulator,
 +                                    domain->voltage, domain->voltage);
 +      }
 +
 +      domain->num_clks = devm_clk_bulk_get_all(domain->dev, &domain->clks);
 +      if (domain->num_clks < 0)
 +              return dev_err_probe(domain->dev, domain->num_clks,
 +                                   "Failed to get domain's clocks\n");
 +
 +      domain->reset = devm_reset_control_array_get_optional_exclusive(domain->dev);
 +      if (IS_ERR(domain->reset))
 +              return dev_err_probe(domain->dev, PTR_ERR(domain->reset),
 +                                   "Failed to get domain's resets\n");
 +
 +      pm_runtime_enable(domain->dev);
 +
 +      if (domain->bits.map)
 +              regmap_update_bits(domain->regmap, domain->regs->map,
 +                                 domain->bits.map, domain->bits.map);
 +
 +      ret = pm_genpd_init(&domain->genpd, NULL, true);
 +      if (ret) {
 +              dev_err(domain->dev, "Failed to init power domain\n");
 +              goto out_domain_unmap;
 +      }
 +
 +      if (IS_ENABLED(CONFIG_LOCKDEP) &&
 +          of_property_read_bool(domain->dev->of_node, "power-domains"))
 +              lockdep_set_subclass(&domain->genpd.mlock, 1);
 +
 +      ret = of_genpd_add_provider_simple(domain->dev->of_node,
 +                                         &domain->genpd);
 +      if (ret) {
 +              dev_err(domain->dev, "Failed to add genpd provider\n");
 +              goto out_genpd_remove;
 +      }
 +
 +      return 0;
 +
 +out_genpd_remove:
 +      pm_genpd_remove(&domain->genpd);
 +out_domain_unmap:
 +      if (domain->bits.map)
 +              regmap_update_bits(domain->regmap, domain->regs->map,
 +                                 domain->bits.map, 0);
 +      pm_runtime_disable(domain->dev);
 +
 +      return ret;
 +}
 +
 +static int imx_pgc_domain_remove(struct platform_device *pdev)
 +{
 +      struct imx_pgc_domain *domain = pdev->dev.platform_data;
 +
 +      of_genpd_del_provider(domain->dev->of_node);
 +      pm_genpd_remove(&domain->genpd);
 +
 +      if (domain->bits.map)
 +              regmap_update_bits(domain->regmap, domain->regs->map,
 +                                 domain->bits.map, 0);
 +
 +      pm_runtime_disable(domain->dev);
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_PM_SLEEP
 +static int imx_pgc_domain_suspend(struct device *dev)
 +{
 +      int ret;
 +
 +      /*
 +       * This may look strange, but is done so the generic PM_SLEEP code
 +       * can power down our domain and more importantly power it up again
 +       * after resume, without tripping over our usage of runtime PM to
 +       * power up/down the nested domains.
 +       */
 +      ret = pm_runtime_get_sync(dev);
 +      if (ret < 0) {
 +              pm_runtime_put_noidle(dev);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static int imx_pgc_domain_resume(struct device *dev)
 +{
 +      return pm_runtime_put(dev);
 +}
 +#endif
 +
 +static const struct dev_pm_ops imx_pgc_domain_pm_ops = {
 +      SET_SYSTEM_SLEEP_PM_OPS(imx_pgc_domain_suspend, imx_pgc_domain_resume)
 +};
 +
 +static const struct platform_device_id imx_pgc_domain_id[] = {
 +      { "imx-pgc-domain", },
 +      { },
 +};
 +
 +static struct platform_driver imx_pgc_domain_driver = {
 +      .driver = {
 +              .name = "imx-pgc",
 +              .pm = &imx_pgc_domain_pm_ops,
 +      },
 +      .probe    = imx_pgc_domain_probe,
 +      .remove   = imx_pgc_domain_remove,
 +      .id_table = imx_pgc_domain_id,
 +};
 +builtin_platform_driver(imx_pgc_domain_driver)
 +
 +static int imx_gpcv2_probe(struct platform_device *pdev)
 +{
 +      const struct imx_pgc_domain_data *domain_data =
 +                      of_device_get_match_data(&pdev->dev);
 +
 +      struct regmap_config regmap_config = {
 +              .reg_bits       = 32,
 +              .val_bits       = 32,
 +              .reg_stride     = 4,
 +              .rd_table       = domain_data->reg_access_table,
 +              .wr_table       = domain_data->reg_access_table,
 +              .max_register   = SZ_4K,
 +      };
 +      struct device *dev = &pdev->dev;
 +      struct device_node *pgc_np, *np;
 +      struct regmap *regmap;
 +      void __iomem *base;
 +      int ret;
 +
 +      pgc_np = of_get_child_by_name(dev->of_node, "pgc");
 +      if (!pgc_np) {
 +              dev_err(dev, "No power domains specified in DT\n");
 +              return -EINVAL;
 +      }
 +
 +      base = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(base))
 +              return PTR_ERR(base);
 +
 +      regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
 +      if (IS_ERR(regmap)) {
 +              ret = PTR_ERR(regmap);
 +              dev_err(dev, "failed to init regmap (%d)\n", ret);
 +              return ret;
 +      }
 +
 +      for_each_child_of_node(pgc_np, np) {
 +              struct platform_device *pd_pdev;
 +              struct imx_pgc_domain *domain;
 +              u32 domain_index;
 +
 +              if (!of_device_is_available(np))
 +                      continue;
 +
 +              ret = of_property_read_u32(np, "reg", &domain_index);
 +              if (ret) {
 +                      dev_err(dev, "Failed to read 'reg' property\n");
 +                      of_node_put(np);
 +                      return ret;
 +              }
 +
 +              if (domain_index >= domain_data->domains_num) {
 +                      dev_warn(dev,
 +                               "Domain index %d is out of bounds\n",
 +                               domain_index);
 +                      continue;
 +              }
 +
 +              pd_pdev = platform_device_alloc("imx-pgc-domain",
 +                                              domain_index);
 +              if (!pd_pdev) {
 +                      dev_err(dev, "Failed to allocate platform device\n");
 +                      of_node_put(np);
 +                      return -ENOMEM;
 +              }
 +
 +              ret = platform_device_add_data(pd_pdev,
 +                                             &domain_data->domains[domain_index],
 +                                             sizeof(domain_data->domains[domain_index]));
 +              if (ret) {
 +                      platform_device_put(pd_pdev);
 +                      of_node_put(np);
 +                      return ret;
 +              }
 +
 +              domain = pd_pdev->dev.platform_data;
 +              domain->regmap = regmap;
 +              domain->regs = domain_data->pgc_regs;
 +
 +              domain->genpd.power_on  = imx_pgc_power_up;
 +              domain->genpd.power_off = imx_pgc_power_down;
 +
 +              pd_pdev->dev.parent = dev;
 +              device_set_node(&pd_pdev->dev, of_fwnode_handle(np));
 +
 +              ret = platform_device_add(pd_pdev);
 +              if (ret) {
 +                      platform_device_put(pd_pdev);
 +                      of_node_put(np);
 +                      return ret;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static const struct of_device_id imx_gpcv2_dt_ids[] = {
 +      { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, },
 +      { .compatible = "fsl,imx8mm-gpc", .data = &imx8mm_pgc_domain_data, },
 +      { .compatible = "fsl,imx8mn-gpc", .data = &imx8mn_pgc_domain_data, },
 +      { .compatible = "fsl,imx8mp-gpc", .data = &imx8mp_pgc_domain_data, },
 +      { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, },
 +      { }
 +};
 +
 +static struct platform_driver imx_gpc_driver = {
 +      .driver = {
 +              .name = "imx-gpcv2",
 +              .of_match_table = imx_gpcv2_dt_ids,
 +      },
 +      .probe = imx_gpcv2_probe,
 +};
 +builtin_platform_driver(imx_gpc_driver)
index afbca0d48c14acc092849f332b4c26ca24fbb2e1,0000000000000000000000000000000000000000..cc5ef6e2f0a8cb141ebb538606b49aef03bae0b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,898 -1,0 +1,899 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0+
 +
 +/*
 + * Copyright 2021 Pengutronix, Lucas Stach <kernel@pengutronix.de>
 + */
 +
 +#include <linux/bitfield.h>
 +#include <linux/device.h>
 +#include <linux/interconnect.h>
 +#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <linux/pm_runtime.h>
 +#include <linux/regmap.h>
 +#include <linux/clk.h>
 +
 +#include <dt-bindings/power/imx8mm-power.h>
 +#include <dt-bindings/power/imx8mn-power.h>
 +#include <dt-bindings/power/imx8mp-power.h>
 +#include <dt-bindings/power/imx8mq-power.h>
 +
 +#define BLK_SFT_RSTN  0x0
 +#define BLK_CLK_EN    0x4
 +#define BLK_MIPI_RESET_DIV    0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
 +
 +struct imx8m_blk_ctrl_domain;
 +
 +struct imx8m_blk_ctrl {
 +      struct device *dev;
 +      struct notifier_block power_nb;
 +      struct device *bus_power_dev;
 +      struct regmap *regmap;
 +      struct imx8m_blk_ctrl_domain *domains;
 +      struct genpd_onecell_data onecell_data;
 +};
 +
 +struct imx8m_blk_ctrl_domain_data {
 +      const char *name;
 +      const char * const *clk_names;
 +      const char * const *path_names;
 +      const char *gpc_name;
 +      int num_clks;
 +      int num_paths;
 +      u32 rst_mask;
 +      u32 clk_mask;
 +
 +      /*
 +       * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register
 +       * which is used to control the reset for the MIPI Phy.
 +       * Since it's only present in certain circumstances,
 +       * an if-statement should be used before setting and clearing this
 +       * register.
 +       */
 +      u32 mipi_phy_rst_mask;
 +};
 +
 +#define DOMAIN_MAX_CLKS 4
 +#define DOMAIN_MAX_PATHS 4
 +
 +struct imx8m_blk_ctrl_domain {
 +      struct generic_pm_domain genpd;
 +      const struct imx8m_blk_ctrl_domain_data *data;
 +      struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
 +      struct icc_bulk_data paths[DOMAIN_MAX_PATHS];
 +      struct device *power_dev;
 +      struct imx8m_blk_ctrl *bc;
 +      int num_paths;
 +};
 +
 +struct imx8m_blk_ctrl_data {
 +      int max_reg;
 +      notifier_fn_t power_notifier_fn;
 +      const struct imx8m_blk_ctrl_domain_data *domains;
 +      int num_domains;
 +};
 +
 +static inline struct imx8m_blk_ctrl_domain *
 +to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd)
 +{
 +      return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd);
 +}
 +
 +static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
 +{
 +      struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
 +      const struct imx8m_blk_ctrl_domain_data *data = domain->data;
 +      struct imx8m_blk_ctrl *bc = domain->bc;
 +      int ret;
 +
 +      /* make sure bus domain is awake */
 +      ret = pm_runtime_get_sync(bc->bus_power_dev);
 +      if (ret < 0) {
 +              pm_runtime_put_noidle(bc->bus_power_dev);
 +              dev_err(bc->dev, "failed to power up bus domain\n");
 +              return ret;
 +      }
 +
 +      /* put devices into reset */
 +      regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
 +      if (data->mipi_phy_rst_mask)
 +              regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
 +
 +      /* enable upstream and blk-ctrl clocks to allow reset to propagate */
 +      ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
 +      if (ret) {
 +              dev_err(bc->dev, "failed to enable clocks\n");
 +              goto bus_put;
 +      }
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
 +
 +      /* power up upstream GPC domain */
 +      ret = pm_runtime_get_sync(domain->power_dev);
 +      if (ret < 0) {
 +              dev_err(bc->dev, "failed to power up peripheral domain\n");
 +              goto clk_disable;
 +      }
 +
 +      /* wait for reset to propagate */
 +      udelay(5);
 +
 +      /* release reset */
 +      regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
 +      if (data->mipi_phy_rst_mask)
 +              regmap_set_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
 +
 +      ret = icc_bulk_set_bw(domain->num_paths, domain->paths);
 +      if (ret)
 +              dev_err(bc->dev, "failed to set icc bw\n");
 +
 +      /* disable upstream clocks */
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +
 +      return 0;
 +
 +clk_disable:
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +bus_put:
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return ret;
 +}
 +
 +static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd)
 +{
 +      struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
 +      const struct imx8m_blk_ctrl_domain_data *data = domain->data;
 +      struct imx8m_blk_ctrl *bc = domain->bc;
 +
 +      /* put devices into reset and disable clocks */
 +      if (data->mipi_phy_rst_mask)
 +              regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
 +
 +      regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
 +      regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
 +
 +      /* power down upstream GPC domain */
 +      pm_runtime_put(domain->power_dev);
 +
 +      /* allow bus domain to suspend */
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return 0;
 +}
 +
 +static struct lock_class_key blk_ctrl_genpd_lock_class;
 +
 +static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
 +{
 +      const struct imx8m_blk_ctrl_data *bc_data;
 +      struct device *dev = &pdev->dev;
 +      struct imx8m_blk_ctrl *bc;
 +      void __iomem *base;
 +      int i, ret;
 +
 +      struct regmap_config regmap_config = {
 +              .reg_bits       = 32,
 +              .val_bits       = 32,
 +              .reg_stride     = 4,
 +      };
 +
 +      bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
 +      if (!bc)
 +              return -ENOMEM;
 +
 +      bc->dev = dev;
 +
 +      bc_data = of_device_get_match_data(dev);
 +
 +      base = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(base))
 +              return PTR_ERR(base);
 +
 +      regmap_config.max_register = bc_data->max_reg;
 +      bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
 +      if (IS_ERR(bc->regmap))
 +              return dev_err_probe(dev, PTR_ERR(bc->regmap),
 +                                   "failed to init regmap\n");
 +
 +      bc->domains = devm_kcalloc(dev, bc_data->num_domains,
 +                                 sizeof(struct imx8m_blk_ctrl_domain),
 +                                 GFP_KERNEL);
 +      if (!bc->domains)
 +              return -ENOMEM;
 +
 +      bc->onecell_data.num_domains = bc_data->num_domains;
 +      bc->onecell_data.domains =
 +              devm_kcalloc(dev, bc_data->num_domains,
 +                           sizeof(struct generic_pm_domain *), GFP_KERNEL);
 +      if (!bc->onecell_data.domains)
 +              return -ENOMEM;
 +
 +      bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
 +      if (IS_ERR(bc->bus_power_dev)) {
 +              if (PTR_ERR(bc->bus_power_dev) == -ENODEV)
 +                      return dev_err_probe(dev, -EPROBE_DEFER,
 +                                           "failed to attach power domain \"bus\"\n");
 +              else
 +                      return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
 +                                           "failed to attach power domain \"bus\"\n");
 +      }
 +
 +      for (i = 0; i < bc_data->num_domains; i++) {
 +              const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i];
 +              struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
 +              int j;
 +
 +              domain->data = data;
 +              domain->num_paths = data->num_paths;
 +
 +              for (j = 0; j < data->num_clks; j++)
 +                      domain->clks[j].id = data->clk_names[j];
 +
 +              for (j = 0; j < data->num_paths; j++) {
 +                      domain->paths[j].name = data->path_names[j];
 +                      /* Fake value for now, just let ICC could configure NoC mode/priority */
 +                      domain->paths[j].avg_bw = 1;
 +                      domain->paths[j].peak_bw = 1;
 +              }
 +
 +              ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths);
 +              if (ret) {
 +                      if (ret != -EPROBE_DEFER) {
 +                              dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n");
 +                              domain->num_paths = 0;
 +                      } else {
 +                              dev_err_probe(dev, ret, "failed to get noc entries\n");
 +                              goto cleanup_pds;
 +                      }
 +              }
 +
 +              ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
 +              if (ret) {
 +                      dev_err_probe(dev, ret, "failed to get clock\n");
 +                      goto cleanup_pds;
 +              }
 +
 +              domain->power_dev =
 +                      dev_pm_domain_attach_by_name(dev, data->gpc_name);
 +              if (IS_ERR(domain->power_dev)) {
 +                      dev_err_probe(dev, PTR_ERR(domain->power_dev),
 +                                    "failed to attach power domain \"%s\"\n",
 +                                    data->gpc_name);
 +                      ret = PTR_ERR(domain->power_dev);
 +                      goto cleanup_pds;
 +              }
 +
 +              domain->genpd.name = data->name;
 +              domain->genpd.power_on = imx8m_blk_ctrl_power_on;
 +              domain->genpd.power_off = imx8m_blk_ctrl_power_off;
 +              domain->bc = bc;
 +
 +              ret = pm_genpd_init(&domain->genpd, NULL, true);
 +              if (ret) {
 +                      dev_err_probe(dev, ret,
 +                                    "failed to init power domain \"%s\"\n",
 +                                    data->gpc_name);
 +                      dev_pm_domain_detach(domain->power_dev, true);
 +                      goto cleanup_pds;
 +              }
 +
 +              /*
 +               * We use runtime PM to trigger power on/off of the upstream GPC
 +               * domain, as a strict hierarchical parent/child power domain
 +               * setup doesn't allow us to meet the sequencing requirements.
 +               * This means we have nested locking of genpd locks, without the
 +               * nesting being visible at the genpd level, so we need a
 +               * separate lock class to make lockdep aware of the fact that
 +               * this are separate domain locks that can be nested without a
 +               * self-deadlock.
 +               */
 +              lockdep_set_class(&domain->genpd.mlock,
 +                                &blk_ctrl_genpd_lock_class);
 +
 +              bc->onecell_data.domains[i] = &domain->genpd;
 +      }
 +
 +      ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
 +      if (ret) {
 +              dev_err_probe(dev, ret, "failed to add power domain provider\n");
 +              goto cleanup_pds;
 +      }
 +
 +      bc->power_nb.notifier_call = bc_data->power_notifier_fn;
 +      ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
 +      if (ret) {
 +              dev_err_probe(dev, ret, "failed to add power notifier\n");
 +              goto cleanup_provider;
 +      }
 +
 +      dev_set_drvdata(dev, bc);
 +
 +      ret = devm_of_platform_populate(dev);
 +      if (ret)
 +              goto cleanup_provider;
 +
 +      return 0;
 +
 +cleanup_provider:
 +      of_genpd_del_provider(dev->of_node);
 +cleanup_pds:
 +      for (i--; i >= 0; i--) {
 +              pm_genpd_remove(&bc->domains[i].genpd);
 +              dev_pm_domain_detach(bc->domains[i].power_dev, true);
 +      }
 +
 +      dev_pm_domain_detach(bc->bus_power_dev, true);
 +
 +      return ret;
 +}
 +
 +static int imx8m_blk_ctrl_remove(struct platform_device *pdev)
 +{
 +      struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
 +      int i;
 +
 +      of_genpd_del_provider(pdev->dev.of_node);
 +
 +      for (i = 0; bc->onecell_data.num_domains; i++) {
 +              struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
 +
 +              pm_genpd_remove(&domain->genpd);
 +              dev_pm_domain_detach(domain->power_dev, true);
 +      }
 +
 +      dev_pm_genpd_remove_notifier(bc->bus_power_dev);
 +
 +      dev_pm_domain_detach(bc->bus_power_dev, true);
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_PM_SLEEP
 +static int imx8m_blk_ctrl_suspend(struct device *dev)
 +{
 +      struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
 +      int ret, i;
 +
 +      /*
 +       * This may look strange, but is done so the generic PM_SLEEP code
 +       * can power down our domains and more importantly power them up again
 +       * after resume, without tripping over our usage of runtime PM to
 +       * control the upstream GPC domains. Things happen in the right order
 +       * in the system suspend/resume paths due to the device parent/child
 +       * hierarchy.
 +       */
 +      ret = pm_runtime_get_sync(bc->bus_power_dev);
 +      if (ret < 0) {
 +              pm_runtime_put_noidle(bc->bus_power_dev);
 +              return ret;
 +      }
 +
 +      for (i = 0; i < bc->onecell_data.num_domains; i++) {
 +              struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
 +
 +              ret = pm_runtime_get_sync(domain->power_dev);
 +              if (ret < 0) {
 +                      pm_runtime_put_noidle(domain->power_dev);
 +                      goto out_fail;
 +              }
 +      }
 +
 +      return 0;
 +
 +out_fail:
 +      for (i--; i >= 0; i--)
 +              pm_runtime_put(bc->domains[i].power_dev);
 +
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return ret;
 +}
 +
 +static int imx8m_blk_ctrl_resume(struct device *dev)
 +{
 +      struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
 +      int i;
 +
 +      for (i = 0; i < bc->onecell_data.num_domains; i++)
 +              pm_runtime_put(bc->domains[i].power_dev);
 +
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return 0;
 +}
 +#endif
 +
 +static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = {
 +      SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume)
 +};
 +
 +static int imx8mm_vpu_power_notifier(struct notifier_block *nb,
 +                                   unsigned long action, void *data)
 +{
 +      struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
 +                                               power_nb);
 +
 +      if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
 +              return NOTIFY_OK;
 +
 +      /*
 +       * The ADB in the VPUMIX domain has no separate reset and clock
 +       * enable bits, but is ungated together with the VPU clocks. To
 +       * allow the handshake with the GPC to progress we put the VPUs
 +       * in reset and ungate the clocks.
 +       */
 +      regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2));
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2));
 +
 +      if (action == GENPD_NOTIFY_ON) {
 +              /*
 +               * On power up we have no software backchannel to the GPC to
 +               * wait for the ADB handshake to happen, so we just delay for a
 +               * bit. On power down the GPC driver waits for the handshake.
 +               */
 +              udelay(5);
 +
 +              /* set "fuse" bits to enable the VPUs */
 +              regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
 +              regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
 +              regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
 +              regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
 +      }
 +
 +      return NOTIFY_OK;
 +}
 +
 +static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = {
 +      [IMX8MM_VPUBLK_PD_G1] = {
 +              .name = "vpublk-g1",
 +              .clk_names = (const char *[]){ "g1", },
 +              .num_clks = 1,
 +              .gpc_name = "g1",
 +              .rst_mask = BIT(1),
 +              .clk_mask = BIT(1),
 +      },
 +      [IMX8MM_VPUBLK_PD_G2] = {
 +              .name = "vpublk-g2",
 +              .clk_names = (const char *[]){ "g2", },
 +              .num_clks = 1,
 +              .gpc_name = "g2",
 +              .rst_mask = BIT(0),
 +              .clk_mask = BIT(0),
 +      },
 +      [IMX8MM_VPUBLK_PD_H1] = {
 +              .name = "vpublk-h1",
 +              .clk_names = (const char *[]){ "h1", },
 +              .num_clks = 1,
 +              .gpc_name = "h1",
 +              .rst_mask = BIT(2),
 +              .clk_mask = BIT(2),
 +      },
 +};
 +
 +static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = {
 +      .max_reg = 0x18,
 +      .power_notifier_fn = imx8mm_vpu_power_notifier,
 +      .domains = imx8mm_vpu_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data),
 +};
 +
 +static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] = {
 +      [IMX8MP_VPUBLK_PD_G1] = {
 +              .name = "vpublk-g1",
 +              .clk_names = (const char *[]){ "g1", },
 +              .num_clks = 1,
 +              .gpc_name = "g1",
 +              .rst_mask = BIT(1),
 +              .clk_mask = BIT(1),
 +              .path_names = (const char *[]){"g1"},
 +              .num_paths = 1,
 +      },
 +      [IMX8MP_VPUBLK_PD_G2] = {
 +              .name = "vpublk-g2",
 +              .clk_names = (const char *[]){ "g2", },
 +              .num_clks = 1,
 +              .gpc_name = "g2",
 +              .rst_mask = BIT(0),
 +              .clk_mask = BIT(0),
 +              .path_names = (const char *[]){"g2"},
 +              .num_paths = 1,
 +      },
 +      [IMX8MP_VPUBLK_PD_VC8000E] = {
 +              .name = "vpublk-vc8000e",
 +              .clk_names = (const char *[]){ "vc8000e", },
 +              .num_clks = 1,
 +              .gpc_name = "vc8000e",
 +              .rst_mask = BIT(2),
 +              .clk_mask = BIT(2),
 +              .path_names = (const char *[]){"vc8000e"},
 +              .num_paths = 1,
 +      },
 +};
 +
 +static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = {
 +      .max_reg = 0x18,
 +      .power_notifier_fn = imx8mm_vpu_power_notifier,
 +      .domains = imx8mp_vpu_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data),
 +};
 +
 +static int imx8mm_disp_power_notifier(struct notifier_block *nb,
 +                                    unsigned long action, void *data)
 +{
 +      struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
 +                                               power_nb);
 +
 +      if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
 +              return NOTIFY_OK;
 +
 +      /* Enable bus clock and deassert bus reset */
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12));
 +      regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6));
 +
 +      /*
 +       * On power up we have no software backchannel to the GPC to
 +       * wait for the ADB handshake to happen, so we just delay for a
 +       * bit. On power down the GPC driver waits for the handshake.
 +       */
 +      if (action == GENPD_NOTIFY_ON)
 +              udelay(5);
 +
 +
 +      return NOTIFY_OK;
 +}
 +
 +static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = {
 +      [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = {
 +              .name = "dispblk-csi-bridge",
 +              .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb",
 +                                             "csi-bridge-core", },
 +              .num_clks = 3,
 +              .gpc_name = "csi-bridge",
 +              .rst_mask = BIT(0) | BIT(1) | BIT(2),
 +              .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
 +      },
 +      [IMX8MM_DISPBLK_PD_LCDIF] = {
 +              .name = "dispblk-lcdif",
 +              .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
 +              .num_clks = 3,
 +              .gpc_name = "lcdif",
 +              .clk_mask = BIT(6) | BIT(7),
 +      },
 +      [IMX8MM_DISPBLK_PD_MIPI_DSI] = {
 +              .name = "dispblk-mipi-dsi",
 +              .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-dsi",
 +              .rst_mask = BIT(5),
 +              .clk_mask = BIT(8) | BIT(9),
 +              .mipi_phy_rst_mask = BIT(17),
 +      },
 +      [IMX8MM_DISPBLK_PD_MIPI_CSI] = {
 +              .name = "dispblk-mipi-csi",
 +              .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-csi",
 +              .rst_mask = BIT(3) | BIT(4),
 +              .clk_mask = BIT(10) | BIT(11),
 +              .mipi_phy_rst_mask = BIT(16),
 +      },
 +};
 +
 +static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = {
 +      .max_reg = 0x2c,
 +      .power_notifier_fn = imx8mm_disp_power_notifier,
 +      .domains = imx8mm_disp_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data),
 +};
 +
 +
 +static int imx8mn_disp_power_notifier(struct notifier_block *nb,
 +                                    unsigned long action, void *data)
 +{
 +      struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
 +                                               power_nb);
 +
 +      if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
 +              return NOTIFY_OK;
 +
 +      /* Enable bus clock and deassert bus reset */
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
 +      regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
 +
 +      /*
 +       * On power up we have no software backchannel to the GPC to
 +       * wait for the ADB handshake to happen, so we just delay for a
 +       * bit. On power down the GPC driver waits for the handshake.
 +       */
 +      if (action == GENPD_NOTIFY_ON)
 +              udelay(5);
 +
 +
 +      return NOTIFY_OK;
 +}
 +
 +static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = {
 +      [IMX8MN_DISPBLK_PD_MIPI_DSI] = {
 +              .name = "dispblk-mipi-dsi",
 +              .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-dsi",
 +              .rst_mask = BIT(0) | BIT(1),
 +              .clk_mask = BIT(0) | BIT(1),
 +              .mipi_phy_rst_mask = BIT(17),
 +      },
 +      [IMX8MN_DISPBLK_PD_MIPI_CSI] = {
 +              .name = "dispblk-mipi-csi",
 +              .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-csi",
 +              .rst_mask = BIT(2) | BIT(3),
 +              .clk_mask = BIT(2) | BIT(3),
 +              .mipi_phy_rst_mask = BIT(16),
 +      },
 +      [IMX8MN_DISPBLK_PD_LCDIF] = {
 +              .name = "dispblk-lcdif",
 +              .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
 +              .num_clks = 3,
 +              .gpc_name = "lcdif",
 +              .rst_mask = BIT(4) | BIT(5),
 +              .clk_mask = BIT(4) | BIT(5),
 +      },
 +      [IMX8MN_DISPBLK_PD_ISI] = {
 +              .name = "dispblk-isi",
 +              .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root",
 +                                              "disp_apb_root"},
 +              .num_clks = 4,
 +              .gpc_name = "isi",
 +              .rst_mask = BIT(6) | BIT(7),
 +              .clk_mask = BIT(6) | BIT(7),
 +      },
 +};
 +
 +static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = {
 +      .max_reg = 0x84,
 +      .power_notifier_fn = imx8mn_disp_power_notifier,
 +      .domains = imx8mn_disp_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data),
 +};
 +
 +#define LCDIF_ARCACHE_CTRL    0x4c
 +#define  LCDIF_1_RD_HURRY     GENMASK(15, 13)
 +#define  LCDIF_0_RD_HURRY     GENMASK(12, 10)
 +
 +static int imx8mp_media_power_notifier(struct notifier_block *nb,
 +                              unsigned long action, void *data)
 +{
 +      struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
 +                                               power_nb);
 +
 +      if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
 +              return NOTIFY_OK;
 +
 +      /* Enable bus clock and deassert bus reset */
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
 +      regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
 +
 +      if (action == GENPD_NOTIFY_ON) {
 +              /*
 +               * On power up we have no software backchannel to the GPC to
 +               * wait for the ADB handshake to happen, so we just delay for a
 +               * bit. On power down the GPC driver waits for the handshake.
 +               */
 +              udelay(5);
 +
 +              /*
 +               * Set panic read hurry level for both LCDIF interfaces to
 +               * maximum priority to minimize chances of display FIFO
 +               * underflow.
 +               */
 +              regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL,
 +                              FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
 +                              FIELD_PREP(LCDIF_0_RD_HURRY, 7));
 +      }
 +
 +      return NOTIFY_OK;
 +}
 +
 +/*
 + * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1,
 + * section 13.2.2, 13.2.3
 + * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks
 + */
 +static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = {
 +      [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = {
 +              .name = "mediablk-mipi-dsi-1",
 +              .clk_names = (const char *[]){ "apb", "phy", },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-dsi1",
 +              .rst_mask = BIT(0) | BIT(1),
 +              .clk_mask = BIT(0) | BIT(1),
 +              .mipi_phy_rst_mask = BIT(17),
 +      },
 +      [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = {
 +              .name = "mediablk-mipi-csi2-1",
 +              .clk_names = (const char *[]){ "apb", "cam1" },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-csi1",
 +              .rst_mask = BIT(2) | BIT(3),
 +              .clk_mask = BIT(2) | BIT(3),
 +              .mipi_phy_rst_mask = BIT(16),
 +      },
 +      [IMX8MP_MEDIABLK_PD_LCDIF_1] = {
 +              .name = "mediablk-lcdif-1",
 +              .clk_names = (const char *[]){ "disp1", "apb", "axi", },
 +              .num_clks = 3,
 +              .gpc_name = "lcdif1",
 +              .rst_mask = BIT(4) | BIT(5) | BIT(23),
 +              .clk_mask = BIT(4) | BIT(5) | BIT(23),
 +              .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"},
 +              .num_paths = 2,
 +      },
 +      [IMX8MP_MEDIABLK_PD_ISI] = {
 +              .name = "mediablk-isi",
 +              .clk_names = (const char *[]){ "axi", "apb" },
 +              .num_clks = 2,
 +              .gpc_name = "isi",
 +              .rst_mask = BIT(6) | BIT(7),
 +              .clk_mask = BIT(6) | BIT(7),
 +              .path_names = (const char *[]){"isi0", "isi1", "isi2"},
 +              .num_paths = 3,
 +      },
 +      [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = {
 +              .name = "mediablk-mipi-csi2-2",
 +              .clk_names = (const char *[]){ "apb", "cam2" },
 +              .num_clks = 2,
 +              .gpc_name = "mipi-csi2",
 +              .rst_mask = BIT(9) | BIT(10),
 +              .clk_mask = BIT(9) | BIT(10),
 +              .mipi_phy_rst_mask = BIT(30),
 +      },
 +      [IMX8MP_MEDIABLK_PD_LCDIF_2] = {
 +              .name = "mediablk-lcdif-2",
 +              .clk_names = (const char *[]){ "disp2", "apb", "axi", },
 +              .num_clks = 3,
 +              .gpc_name = "lcdif2",
 +              .rst_mask = BIT(11) | BIT(12) | BIT(24),
 +              .clk_mask = BIT(11) | BIT(12) | BIT(24),
 +              .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"},
 +              .num_paths = 2,
 +      },
 +      [IMX8MP_MEDIABLK_PD_ISP] = {
 +              .name = "mediablk-isp",
 +              .clk_names = (const char *[]){ "isp", "axi", "apb" },
 +              .num_clks = 3,
 +              .gpc_name = "isp",
 +              .rst_mask = BIT(16) | BIT(17) | BIT(18),
 +              .clk_mask = BIT(16) | BIT(17) | BIT(18),
 +              .path_names = (const char *[]){"isp0", "isp1"},
 +              .num_paths = 2,
 +      },
 +      [IMX8MP_MEDIABLK_PD_DWE] = {
 +              .name = "mediablk-dwe",
 +              .clk_names = (const char *[]){ "axi", "apb" },
 +              .num_clks = 2,
 +              .gpc_name = "dwe",
 +              .rst_mask = BIT(19) | BIT(20) | BIT(21),
 +              .clk_mask = BIT(19) | BIT(20) | BIT(21),
 +              .path_names = (const char *[]){"dwe"},
 +              .num_paths = 1,
 +      },
 +      [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = {
 +              .name = "mediablk-mipi-dsi-2",
 +              .clk_names = (const char *[]){ "phy", },
 +              .num_clks = 1,
 +              .gpc_name = "mipi-dsi2",
 +              .rst_mask = BIT(22),
 +              .clk_mask = BIT(22),
 +              .mipi_phy_rst_mask = BIT(29),
 +      },
 +};
 +
 +static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = {
 +      .max_reg = 0x138,
 +      .power_notifier_fn = imx8mp_media_power_notifier,
 +      .domains = imx8mp_media_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data),
 +};
 +
 +static int imx8mq_vpu_power_notifier(struct notifier_block *nb,
 +                                   unsigned long action, void *data)
 +{
 +      struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
 +                                               power_nb);
 +
 +      if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
 +              return NOTIFY_OK;
 +
 +      /*
 +       * The ADB in the VPUMIX domain has no separate reset and clock
 +       * enable bits, but is ungated and reset together with the VPUs. The
 +       * reset and clock enable inputs to the ADB is a logical OR of the
 +       * VPU bits. In order to set the G2 fuse bits, the G2 clock must
 +       * also be enabled.
 +       */
 +      regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1));
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1));
 +
 +      if (action == GENPD_NOTIFY_ON) {
 +              /*
 +               * On power up we have no software backchannel to the GPC to
 +               * wait for the ADB handshake to happen, so we just delay for a
 +               * bit. On power down the GPC driver waits for the handshake.
 +               */
 +              udelay(5);
 +
 +              /* set "fuse" bits to enable the VPUs */
 +              regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
 +              regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
 +              regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
 +      }
 +
 +      return NOTIFY_OK;
 +}
 +
 +static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = {
 +      [IMX8MQ_VPUBLK_PD_G1] = {
 +              .name = "vpublk-g1",
 +              .clk_names = (const char *[]){ "g1", },
 +              .num_clks = 1,
 +              .gpc_name = "g1",
 +              .rst_mask = BIT(1),
 +              .clk_mask = BIT(1),
 +      },
 +      [IMX8MQ_VPUBLK_PD_G2] = {
 +              .name = "vpublk-g2",
 +              .clk_names = (const char *[]){ "g2", },
 +              .num_clks = 1,
 +              .gpc_name = "g2",
 +              .rst_mask = BIT(0),
 +              .clk_mask = BIT(0),
 +      },
 +};
 +
 +static const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = {
 +      .max_reg = 0x14,
 +      .power_notifier_fn = imx8mq_vpu_power_notifier,
 +      .domains = imx8mq_vpu_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data),
 +};
 +
 +static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
 +      {
 +              .compatible = "fsl,imx8mm-vpu-blk-ctrl",
 +              .data = &imx8mm_vpu_blk_ctl_dev_data
 +      }, {
 +              .compatible = "fsl,imx8mm-disp-blk-ctrl",
 +              .data = &imx8mm_disp_blk_ctl_dev_data
 +      }, {
 +              .compatible = "fsl,imx8mn-disp-blk-ctrl",
 +              .data = &imx8mn_disp_blk_ctl_dev_data
 +      }, {
 +              .compatible = "fsl,imx8mp-media-blk-ctrl",
 +              .data = &imx8mp_media_blk_ctl_dev_data
 +      }, {
 +              .compatible = "fsl,imx8mq-vpu-blk-ctrl",
 +              .data = &imx8mq_vpu_blk_ctl_dev_data
 +      }, {
 +              .compatible = "fsl,imx8mp-vpu-blk-ctrl",
 +              .data = &imx8mp_vpu_blk_ctl_dev_data
 +      }, {
 +              /* Sentinel */
 +      }
 +};
 +MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match);
 +
 +static struct platform_driver imx8m_blk_ctrl_driver = {
 +      .probe = imx8m_blk_ctrl_probe,
 +      .remove = imx8m_blk_ctrl_remove,
 +      .driver = {
 +              .name = "imx8m-blk-ctrl",
 +              .pm = &imx8m_blk_ctrl_pm_ops,
 +              .of_match_table = imx8m_blk_ctrl_of_match,
 +      },
 +};
 +module_platform_driver(imx8m_blk_ctrl_driver);
 +MODULE_LICENSE("GPL");
index 870aecc0202aebd88c9af6d62a4cb94aa8f57401,0000000000000000000000000000000000000000..5a9f5ece43d9bd98fa15f0f8fc6b167a2322901b
mode 100644,000000..100644
--- /dev/null
@@@ -1,867 -1,0 +1,867 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0+
 +
 +/*
 + * Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
 + */
 +
 +#include <linux/bitfield.h>
 +#include <linux/clk.h>
 +#include <linux/clk-provider.h>
 +#include <linux/device.h>
 +#include <linux/interconnect.h>
 +#include <linux/module.h>
++#include <linux/of.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <linux/pm_runtime.h>
 +#include <linux/regmap.h>
 +
 +#include <dt-bindings/power/imx8mp-power.h>
 +
 +#define GPR_REG0              0x0
 +#define  PCIE_CLOCK_MODULE_EN BIT(0)
 +#define  USB_CLOCK_MODULE_EN  BIT(1)
 +#define  PCIE_PHY_APB_RST     BIT(4)
 +#define  PCIE_PHY_INIT_RST    BIT(5)
 +#define GPR_REG1              0x4
 +#define  PLL_LOCK             BIT(13)
 +#define GPR_REG2              0x8
 +#define  P_PLL_MASK           GENMASK(5, 0)
 +#define  M_PLL_MASK           GENMASK(15, 6)
 +#define  S_PLL_MASK           GENMASK(18, 16)
 +#define GPR_REG3              0xc
 +#define  PLL_CKE              BIT(17)
 +#define  PLL_RST              BIT(31)
 +
 +struct imx8mp_blk_ctrl_domain;
 +
 +struct imx8mp_blk_ctrl {
 +      struct device *dev;
 +      struct notifier_block power_nb;
 +      struct device *bus_power_dev;
 +      struct regmap *regmap;
 +      struct imx8mp_blk_ctrl_domain *domains;
 +      struct genpd_onecell_data onecell_data;
 +      void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 +      void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 +};
 +
 +struct imx8mp_blk_ctrl_domain_data {
 +      const char *name;
 +      const char * const *clk_names;
 +      int num_clks;
 +      const char * const *path_names;
 +      int num_paths;
 +      const char *gpc_name;
 +};
 +
 +#define DOMAIN_MAX_CLKS 2
 +#define DOMAIN_MAX_PATHS 3
 +
 +struct imx8mp_blk_ctrl_domain {
 +      struct generic_pm_domain genpd;
 +      const struct imx8mp_blk_ctrl_domain_data *data;
 +      struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
 +      struct icc_bulk_data paths[DOMAIN_MAX_PATHS];
 +      struct device *power_dev;
 +      struct imx8mp_blk_ctrl *bc;
 +      int num_paths;
 +      int id;
 +};
 +
 +struct imx8mp_blk_ctrl_data {
 +      int max_reg;
 +      int (*probe) (struct imx8mp_blk_ctrl *bc);
 +      notifier_fn_t power_notifier_fn;
 +      void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 +      void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 +      const struct imx8mp_blk_ctrl_domain_data *domains;
 +      int num_domains;
 +};
 +
 +static inline struct imx8mp_blk_ctrl_domain *
 +to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd)
 +{
 +      return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd);
 +}
 +
 +struct clk_hsio_pll {
 +      struct clk_hw   hw;
 +      struct regmap *regmap;
 +};
 +
 +static inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw)
 +{
 +      return container_of(hw, struct clk_hsio_pll, hw);
 +}
 +
 +static int clk_hsio_pll_prepare(struct clk_hw *hw)
 +{
 +      struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
 +      u32 val;
 +
 +      /* set the PLL configuration */
 +      regmap_update_bits(clk->regmap, GPR_REG2,
 +                         P_PLL_MASK | M_PLL_MASK | S_PLL_MASK,
 +                         FIELD_PREP(P_PLL_MASK, 12) |
 +                         FIELD_PREP(M_PLL_MASK, 800) |
 +                         FIELD_PREP(S_PLL_MASK, 4));
 +
 +      /* de-assert PLL reset */
 +      regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST);
 +
 +      /* enable PLL */
 +      regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE);
 +
 +      return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val,
 +                                      val & PLL_LOCK, 10, 100);
 +}
 +
 +static void clk_hsio_pll_unprepare(struct clk_hw *hw)
 +{
 +      struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
 +
 +      regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0);
 +}
 +
 +static int clk_hsio_pll_is_prepared(struct clk_hw *hw)
 +{
 +      struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
 +
 +      return regmap_test_bits(clk->regmap, GPR_REG1, PLL_LOCK);
 +}
 +
 +static unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw,
 +                                            unsigned long parent_rate)
 +{
 +      return 100000000;
 +}
 +
 +static const struct clk_ops clk_hsio_pll_ops = {
 +      .prepare = clk_hsio_pll_prepare,
 +      .unprepare = clk_hsio_pll_unprepare,
 +      .is_prepared = clk_hsio_pll_is_prepared,
 +      .recalc_rate = clk_hsio_pll_recalc_rate,
 +};
 +
 +static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc)
 +{
 +      struct clk_hsio_pll *clk_hsio_pll;
 +      struct clk_hw *hw;
 +      struct clk_init_data init = {};
 +      int ret;
 +
 +      clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL);
 +      if (!clk_hsio_pll)
 +              return -ENOMEM;
 +
 +      init.name = "hsio_pll";
 +      init.ops = &clk_hsio_pll_ops;
 +      init.parent_names = (const char *[]){"osc_24m"};
 +      init.num_parents = 1;
 +
 +      clk_hsio_pll->regmap = bc->regmap;
 +      clk_hsio_pll->hw.init = &init;
 +
 +      hw = &clk_hsio_pll->hw;
 +      ret = devm_clk_hw_register(bc->dev, hw);
 +      if (ret)
 +              return ret;
 +
 +      return devm_of_clk_add_hw_provider(bc->dev, of_clk_hw_simple_get, hw);
 +}
 +
 +static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
 +                                        struct imx8mp_blk_ctrl_domain *domain)
 +{
 +      switch (domain->id) {
 +      case IMX8MP_HSIOBLK_PD_USB:
 +              regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
 +              break;
 +      case IMX8MP_HSIOBLK_PD_PCIE:
 +              regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
 +              break;
 +      case IMX8MP_HSIOBLK_PD_PCIE_PHY:
 +              regmap_set_bits(bc->regmap, GPR_REG0,
 +                              PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
 +                                         struct imx8mp_blk_ctrl_domain *domain)
 +{
 +      switch (domain->id) {
 +      case IMX8MP_HSIOBLK_PD_USB:
 +              regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
 +              break;
 +      case IMX8MP_HSIOBLK_PD_PCIE:
 +              regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
 +              break;
 +      case IMX8MP_HSIOBLK_PD_PCIE_PHY:
 +              regmap_clear_bits(bc->regmap, GPR_REG0,
 +                                PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static int imx8mp_hsio_power_notifier(struct notifier_block *nb,
 +                                    unsigned long action, void *data)
 +{
 +      struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl,
 +                                               power_nb);
 +      struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks;
 +      int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks;
 +      int ret;
 +
 +      switch (action) {
 +      case GENPD_NOTIFY_ON:
 +              /*
 +               * enable USB clock for a moment for the power-on ADB handshake
 +               * to proceed
 +               */
 +              ret = clk_bulk_prepare_enable(num_clks, usb_clk);
 +              if (ret)
 +                      return NOTIFY_BAD;
 +              regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
 +
 +              udelay(5);
 +
 +              regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
 +              clk_bulk_disable_unprepare(num_clks, usb_clk);
 +              break;
 +      case GENPD_NOTIFY_PRE_OFF:
 +              /* enable USB clock for the power-down ADB handshake to work */
 +              ret = clk_bulk_prepare_enable(num_clks, usb_clk);
 +              if (ret)
 +                      return NOTIFY_BAD;
 +
 +              regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
 +              break;
 +      case GENPD_NOTIFY_OFF:
 +              clk_bulk_disable_unprepare(num_clks, usb_clk);
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      return NOTIFY_OK;
 +}
 +
 +static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = {
 +      [IMX8MP_HSIOBLK_PD_USB] = {
 +              .name = "hsioblk-usb",
 +              .clk_names = (const char *[]){ "usb" },
 +              .num_clks = 1,
 +              .gpc_name = "usb",
 +              .path_names = (const char *[]){"usb1", "usb2"},
 +              .num_paths = 2,
 +      },
 +      [IMX8MP_HSIOBLK_PD_USB_PHY1] = {
 +              .name = "hsioblk-usb-phy1",
 +              .gpc_name = "usb-phy1",
 +      },
 +      [IMX8MP_HSIOBLK_PD_USB_PHY2] = {
 +              .name = "hsioblk-usb-phy2",
 +              .gpc_name = "usb-phy2",
 +      },
 +      [IMX8MP_HSIOBLK_PD_PCIE] = {
 +              .name = "hsioblk-pcie",
 +              .clk_names = (const char *[]){ "pcie" },
 +              .num_clks = 1,
 +              .gpc_name = "pcie",
 +              .path_names = (const char *[]){"noc-pcie", "pcie"},
 +              .num_paths = 2,
 +      },
 +      [IMX8MP_HSIOBLK_PD_PCIE_PHY] = {
 +              .name = "hsioblk-pcie-phy",
 +              .gpc_name = "pcie-phy",
 +      },
 +};
 +
 +static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = {
 +      .max_reg = 0x24,
 +      .probe = imx8mp_hsio_blk_ctrl_probe,
 +      .power_on = imx8mp_hsio_blk_ctrl_power_on,
 +      .power_off = imx8mp_hsio_blk_ctrl_power_off,
 +      .power_notifier_fn = imx8mp_hsio_power_notifier,
 +      .domains = imx8mp_hsio_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data),
 +};
 +
 +#define HDMI_RTX_RESET_CTL0   0x20
 +#define HDMI_RTX_CLK_CTL0     0x40
 +#define HDMI_RTX_CLK_CTL1     0x50
 +#define HDMI_RTX_CLK_CTL2     0x60
 +#define HDMI_RTX_CLK_CTL3     0x70
 +#define HDMI_RTX_CLK_CTL4     0x80
 +#define HDMI_TX_CONTROL0      0x200
 +#define  HDMI_LCDIF_NOC_HURRY_MASK            GENMASK(14, 12)
 +
 +static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
 +                                        struct imx8mp_blk_ctrl_domain *domain)
 +{
 +      switch (domain->id) {
 +      case IMX8MP_HDMIBLK_PD_IRQSTEER:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_LCDIF:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
 +                              BIT(16) | BIT(17) | BIT(18) |
 +                              BIT(19) | BIT(20));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
 +                              BIT(4) | BIT(5) | BIT(6));
 +              regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0,
 +                              FIELD_PREP(HDMI_LCDIF_NOC_HURRY_MASK, 7));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_PAI:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_PVI:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_TRNG:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HDMI_TX:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
 +                              BIT(2) | BIT(4) | BIT(5));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1,
 +                              BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
 +                              BIT(18) | BIT(19) | BIT(20) | BIT(21));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
 +                              BIT(7) | BIT(10) | BIT(11));
 +              regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
 +              regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HDCP:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HRV:
 +              regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5));
 +              regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15));
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
 +                                         struct imx8mp_blk_ctrl_domain *domain)
 +{
 +      switch (domain->id) {
 +      case IMX8MP_HDMIBLK_PD_IRQSTEER:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_LCDIF:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
 +                                BIT(4) | BIT(5) | BIT(6));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
 +                                BIT(16) | BIT(17) | BIT(18) |
 +                                BIT(19) | BIT(20));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_PAI:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_PVI:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_TRNG:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HDMI_TX:
 +              regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
 +                                BIT(7) | BIT(10) | BIT(11));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1,
 +                                BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
 +                                BIT(18) | BIT(19) | BIT(20) | BIT(21));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
 +                                BIT(2) | BIT(4) | BIT(5));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
 +              regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HDCP:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11));
 +              break;
 +      case IMX8MP_HDMIBLK_PD_HRV:
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15));
 +              regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5));
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static int imx8mp_hdmi_power_notifier(struct notifier_block *nb,
 +                                    unsigned long action, void *data)
 +{
 +      struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl,
 +                                               power_nb);
 +
 +      if (action != GENPD_NOTIFY_ON)
 +              return NOTIFY_OK;
 +
 +      /*
 +       * Contrary to other blk-ctrls the reset and clock don't clear when the
 +       * power domain is powered down. To ensure the proper reset pulsing,
 +       * first clear them all to asserted state, then enable the bus clocks
 +       * and then release the ADB reset.
 +       */
 +      regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0);
 +      regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0);
 +      regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0);
 +      regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
 +                      BIT(0) | BIT(1) | BIT(10));
 +      regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0));
 +
 +      /*
 +       * On power up we have no software backchannel to the GPC to
 +       * wait for the ADB handshake to happen, so we just delay for a
 +       * bit. On power down the GPC driver waits for the handshake.
 +       */
 +      udelay(5);
 +
 +      return NOTIFY_OK;
 +}
 +
 +static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = {
 +      [IMX8MP_HDMIBLK_PD_IRQSTEER] = {
 +              .name = "hdmiblk-irqsteer",
 +              .clk_names = (const char *[]){ "apb" },
 +              .num_clks = 1,
 +              .gpc_name = "irqsteer",
 +      },
 +      [IMX8MP_HDMIBLK_PD_LCDIF] = {
 +              .name = "hdmiblk-lcdif",
 +              .clk_names = (const char *[]){ "axi", "apb" },
 +              .num_clks = 2,
 +              .gpc_name = "lcdif",
 +              .path_names = (const char *[]){"lcdif-hdmi"},
 +              .num_paths = 1,
 +      },
 +      [IMX8MP_HDMIBLK_PD_PAI] = {
 +              .name = "hdmiblk-pai",
 +              .clk_names = (const char *[]){ "apb" },
 +              .num_clks = 1,
 +              .gpc_name = "pai",
 +      },
 +      [IMX8MP_HDMIBLK_PD_PVI] = {
 +              .name = "hdmiblk-pvi",
 +              .clk_names = (const char *[]){ "apb" },
 +              .num_clks = 1,
 +              .gpc_name = "pvi",
 +      },
 +      [IMX8MP_HDMIBLK_PD_TRNG] = {
 +              .name = "hdmiblk-trng",
 +              .clk_names = (const char *[]){ "apb" },
 +              .num_clks = 1,
 +              .gpc_name = "trng",
 +      },
 +      [IMX8MP_HDMIBLK_PD_HDMI_TX] = {
 +              .name = "hdmiblk-hdmi-tx",
 +              .clk_names = (const char *[]){ "apb", "ref_266m" },
 +              .num_clks = 2,
 +              .gpc_name = "hdmi-tx",
 +      },
 +      [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = {
 +              .name = "hdmiblk-hdmi-tx-phy",
 +              .clk_names = (const char *[]){ "apb", "ref_24m" },
 +              .num_clks = 2,
 +              .gpc_name = "hdmi-tx-phy",
 +      },
 +      [IMX8MP_HDMIBLK_PD_HRV] = {
 +              .name = "hdmiblk-hrv",
 +              .clk_names = (const char *[]){ "axi", "apb" },
 +              .num_clks = 2,
 +              .gpc_name = "hrv",
 +              .path_names = (const char *[]){"hrv"},
 +              .num_paths = 1,
 +      },
 +      [IMX8MP_HDMIBLK_PD_HDCP] = {
 +              .name = "hdmiblk-hdcp",
 +              .clk_names = (const char *[]){ "axi", "apb" },
 +              .num_clks = 2,
 +              .gpc_name = "hdcp",
 +              .path_names = (const char *[]){"hdcp"},
 +              .num_paths = 1,
 +      },
 +};
 +
 +static const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = {
 +      .max_reg = 0x23c,
 +      .power_on = imx8mp_hdmi_blk_ctrl_power_on,
 +      .power_off = imx8mp_hdmi_blk_ctrl_power_off,
 +      .power_notifier_fn = imx8mp_hdmi_power_notifier,
 +      .domains = imx8mp_hdmi_domain_data,
 +      .num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data),
 +};
 +
 +static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
 +{
 +      struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
 +      const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
 +      struct imx8mp_blk_ctrl *bc = domain->bc;
 +      int ret;
 +
 +      /* make sure bus domain is awake */
 +      ret = pm_runtime_resume_and_get(bc->bus_power_dev);
 +      if (ret < 0) {
 +              dev_err(bc->dev, "failed to power up bus domain\n");
 +              return ret;
 +      }
 +
 +      /* enable upstream clocks */
 +      ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
 +      if (ret) {
 +              dev_err(bc->dev, "failed to enable clocks\n");
 +              goto bus_put;
 +      }
 +
 +      /* domain specific blk-ctrl manipulation */
 +      bc->power_on(bc, domain);
 +
 +      /* power up upstream GPC domain */
 +      ret = pm_runtime_resume_and_get(domain->power_dev);
 +      if (ret < 0) {
 +              dev_err(bc->dev, "failed to power up peripheral domain\n");
 +              goto clk_disable;
 +      }
 +
 +      ret = icc_bulk_set_bw(domain->num_paths, domain->paths);
 +      if (ret)
 +              dev_err(bc->dev, "failed to set icc bw\n");
 +
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +
 +      return 0;
 +
 +clk_disable:
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +bus_put:
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return ret;
 +}
 +
 +static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd)
 +{
 +      struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
 +      const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
 +      struct imx8mp_blk_ctrl *bc = domain->bc;
 +      int ret;
 +
 +      ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
 +      if (ret) {
 +              dev_err(bc->dev, "failed to enable clocks\n");
 +              return ret;
 +      }
 +
 +      /* domain specific blk-ctrl manipulation */
 +      bc->power_off(bc, domain);
 +
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +
 +      /* power down upstream GPC domain */
 +      pm_runtime_put(domain->power_dev);
 +
 +      /* allow bus domain to suspend */
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return 0;
 +}
 +
 +static struct lock_class_key blk_ctrl_genpd_lock_class;
 +
 +static int imx8mp_blk_ctrl_probe(struct platform_device *pdev)
 +{
 +      const struct imx8mp_blk_ctrl_data *bc_data;
 +      struct device *dev = &pdev->dev;
 +      struct imx8mp_blk_ctrl *bc;
 +      void __iomem *base;
 +      int num_domains, i, ret;
 +
 +      struct regmap_config regmap_config = {
 +              .reg_bits       = 32,
 +              .val_bits       = 32,
 +              .reg_stride     = 4,
 +      };
 +
 +      bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
 +      if (!bc)
 +              return -ENOMEM;
 +
 +      bc->dev = dev;
 +
 +      bc_data = of_device_get_match_data(dev);
 +      num_domains = bc_data->num_domains;
 +
 +      base = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(base))
 +              return PTR_ERR(base);
 +
 +      regmap_config.max_register = bc_data->max_reg;
 +      bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
 +      if (IS_ERR(bc->regmap))
 +              return dev_err_probe(dev, PTR_ERR(bc->regmap),
 +                                   "failed to init regmap\n");
 +
 +      bc->domains = devm_kcalloc(dev, num_domains,
 +                                 sizeof(struct imx8mp_blk_ctrl_domain),
 +                                 GFP_KERNEL);
 +      if (!bc->domains)
 +              return -ENOMEM;
 +
 +      bc->onecell_data.num_domains = num_domains;
 +      bc->onecell_data.domains =
 +              devm_kcalloc(dev, num_domains,
 +                           sizeof(struct generic_pm_domain *), GFP_KERNEL);
 +      if (!bc->onecell_data.domains)
 +              return -ENOMEM;
 +
 +      bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
 +      if (IS_ERR(bc->bus_power_dev))
 +              return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
 +                                   "failed to attach bus power domain\n");
 +
 +      bc->power_off = bc_data->power_off;
 +      bc->power_on = bc_data->power_on;
 +
 +      for (i = 0; i < num_domains; i++) {
 +              const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i];
 +              struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
 +              int j;
 +
 +              domain->data = data;
 +              domain->num_paths = data->num_paths;
 +
 +              for (j = 0; j < data->num_clks; j++)
 +                      domain->clks[j].id = data->clk_names[j];
 +
 +              for (j = 0; j < data->num_paths; j++) {
 +                      domain->paths[j].name = data->path_names[j];
 +                      /* Fake value for now, just let ICC could configure NoC mode/priority */
 +                      domain->paths[j].avg_bw = 1;
 +                      domain->paths[j].peak_bw = 1;
 +              }
 +
 +              ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths);
 +              if (ret) {
 +                      if (ret != -EPROBE_DEFER) {
 +                              dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n");
 +                              domain->num_paths = 0;
 +                      } else {
 +                              dev_err_probe(dev, ret, "failed to get noc entries\n");
 +                              goto cleanup_pds;
 +                      }
 +              }
 +
 +              ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
 +              if (ret) {
 +                      dev_err_probe(dev, ret, "failed to get clock\n");
 +                      goto cleanup_pds;
 +              }
 +
 +              domain->power_dev =
 +                      dev_pm_domain_attach_by_name(dev, data->gpc_name);
 +              if (IS_ERR(domain->power_dev)) {
 +                      dev_err_probe(dev, PTR_ERR(domain->power_dev),
 +                                    "failed to attach power domain %s\n",
 +                                    data->gpc_name);
 +                      ret = PTR_ERR(domain->power_dev);
 +                      goto cleanup_pds;
 +              }
 +
 +              domain->genpd.name = data->name;
 +              domain->genpd.power_on = imx8mp_blk_ctrl_power_on;
 +              domain->genpd.power_off = imx8mp_blk_ctrl_power_off;
 +              domain->bc = bc;
 +              domain->id = i;
 +
 +              ret = pm_genpd_init(&domain->genpd, NULL, true);
 +              if (ret) {
 +                      dev_err_probe(dev, ret, "failed to init power domain\n");
 +                      dev_pm_domain_detach(domain->power_dev, true);
 +                      goto cleanup_pds;
 +              }
 +
 +              /*
 +               * We use runtime PM to trigger power on/off of the upstream GPC
 +               * domain, as a strict hierarchical parent/child power domain
 +               * setup doesn't allow us to meet the sequencing requirements.
 +               * This means we have nested locking of genpd locks, without the
 +               * nesting being visible at the genpd level, so we need a
 +               * separate lock class to make lockdep aware of the fact that
 +               * this are separate domain locks that can be nested without a
 +               * self-deadlock.
 +               */
 +              lockdep_set_class(&domain->genpd.mlock,
 +                                &blk_ctrl_genpd_lock_class);
 +
 +              bc->onecell_data.domains[i] = &domain->genpd;
 +      }
 +
 +      ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
 +      if (ret) {
 +              dev_err_probe(dev, ret, "failed to add power domain provider\n");
 +              goto cleanup_pds;
 +      }
 +
 +      bc->power_nb.notifier_call = bc_data->power_notifier_fn;
 +      ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
 +      if (ret) {
 +              dev_err_probe(dev, ret, "failed to add power notifier\n");
 +              goto cleanup_provider;
 +      }
 +
 +      if (bc_data->probe) {
 +              ret = bc_data->probe(bc);
 +              if (ret)
 +                      goto cleanup_provider;
 +      }
 +
 +      dev_set_drvdata(dev, bc);
 +
 +      return 0;
 +
 +cleanup_provider:
 +      of_genpd_del_provider(dev->of_node);
 +cleanup_pds:
 +      for (i--; i >= 0; i--) {
 +              pm_genpd_remove(&bc->domains[i].genpd);
 +              dev_pm_domain_detach(bc->domains[i].power_dev, true);
 +      }
 +
 +      dev_pm_domain_detach(bc->bus_power_dev, true);
 +
 +      return ret;
 +}
 +
 +static int imx8mp_blk_ctrl_remove(struct platform_device *pdev)
 +{
 +      struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
 +      int i;
 +
 +      of_genpd_del_provider(pdev->dev.of_node);
 +
 +      for (i = 0; bc->onecell_data.num_domains; i++) {
 +              struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
 +
 +              pm_genpd_remove(&domain->genpd);
 +              dev_pm_domain_detach(domain->power_dev, true);
 +      }
 +
 +      dev_pm_genpd_remove_notifier(bc->bus_power_dev);
 +
 +      dev_pm_domain_detach(bc->bus_power_dev, true);
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_PM_SLEEP
 +static int imx8mp_blk_ctrl_suspend(struct device *dev)
 +{
 +      struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev);
 +      int ret, i;
 +
 +      /*
 +       * This may look strange, but is done so the generic PM_SLEEP code
 +       * can power down our domains and more importantly power them up again
 +       * after resume, without tripping over our usage of runtime PM to
 +       * control the upstream GPC domains. Things happen in the right order
 +       * in the system suspend/resume paths due to the device parent/child
 +       * hierarchy.
 +       */
 +      ret = pm_runtime_get_sync(bc->bus_power_dev);
 +      if (ret < 0) {
 +              pm_runtime_put_noidle(bc->bus_power_dev);
 +              return ret;
 +      }
 +
 +      for (i = 0; i < bc->onecell_data.num_domains; i++) {
 +              struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
 +
 +              ret = pm_runtime_get_sync(domain->power_dev);
 +              if (ret < 0) {
 +                      pm_runtime_put_noidle(domain->power_dev);
 +                      goto out_fail;
 +              }
 +      }
 +
 +      return 0;
 +
 +out_fail:
 +      for (i--; i >= 0; i--)
 +              pm_runtime_put(bc->domains[i].power_dev);
 +
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return ret;
 +}
 +
 +static int imx8mp_blk_ctrl_resume(struct device *dev)
 +{
 +      struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev);
 +      int i;
 +
 +      for (i = 0; i < bc->onecell_data.num_domains; i++)
 +              pm_runtime_put(bc->domains[i].power_dev);
 +
 +      pm_runtime_put(bc->bus_power_dev);
 +
 +      return 0;
 +}
 +#endif
 +
 +static const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = {
 +      SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend,
 +                              imx8mp_blk_ctrl_resume)
 +};
 +
 +static const struct of_device_id imx8mp_blk_ctrl_of_match[] = {
 +      {
 +              .compatible = "fsl,imx8mp-hsio-blk-ctrl",
 +              .data = &imx8mp_hsio_blk_ctl_dev_data,
 +      }, {
 +              .compatible = "fsl,imx8mp-hdmi-blk-ctrl",
 +              .data = &imx8mp_hdmi_blk_ctl_dev_data,
 +      }, {
 +              /* Sentinel */
 +      }
 +};
 +MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match);
 +
 +static struct platform_driver imx8mp_blk_ctrl_driver = {
 +      .probe = imx8mp_blk_ctrl_probe,
 +      .remove = imx8mp_blk_ctrl_remove,
 +      .driver = {
 +              .name = "imx8mp-blk-ctrl",
 +              .pm = &imx8mp_blk_ctrl_pm_ops,
 +              .of_match_table = imx8mp_blk_ctrl_of_match,
 +      },
 +};
 +module_platform_driver(imx8mp_blk_ctrl_driver);
 +MODULE_LICENSE("GPL");
index 2c600329436cf288250e76df5d91fd1b8851dacb,0000000000000000000000000000000000000000..40bd90f8b977b65894d7ddd4cd3bbd065ea3280f
mode 100644,000000..100644
--- /dev/null
@@@ -1,436 -1,0 +1,451 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0
 +/*
 + * Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com>
 + */
 +
 +#include <linux/clk.h>
 +#include <linux/device.h>
 +#include <linux/module.h>
++#include <linux/of.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +#include <linux/pm_runtime.h>
 +#include <linux/regmap.h>
 +#include <linux/sizes.h>
 +
 +#include <dt-bindings/power/fsl,imx93-power.h>
 +
 +#define BLK_SFT_RSTN  0x0
 +#define BLK_CLK_EN    0x4
 +#define BLK_MAX_CLKS  4
 +
 +#define DOMAIN_MAX_CLKS 4
 +
 +#define LCDIF_QOS_REG         0xC
 +#define LCDIF_DEFAULT_QOS_OFF 12
 +#define LCDIF_CFG_QOS_OFF     8
 +
 +#define PXP_QOS_REG           0x10
 +#define PXP_R_DEFAULT_QOS_OFF 28
 +#define PXP_R_CFG_QOS_OFF     24
 +#define PXP_W_DEFAULT_QOS_OFF 20
 +#define PXP_W_CFG_QOS_OFF     16
 +
 +#define ISI_CACHE_REG         0x14
 +
 +#define ISI_QOS_REG           0x1C
 +#define ISI_V_DEFAULT_QOS_OFF 28
 +#define ISI_V_CFG_QOS_OFF     24
 +#define ISI_U_DEFAULT_QOS_OFF 20
 +#define ISI_U_CFG_QOS_OFF     16
 +#define ISI_Y_R_DEFAULT_QOS_OFF       12
 +#define ISI_Y_R_CFG_QOS_OFF   8
 +#define ISI_Y_W_DEFAULT_QOS_OFF       4
 +#define ISI_Y_W_CFG_QOS_OFF   0
 +
 +#define PRIO_MASK             0xF
 +
 +#define PRIO(X)                       (X)
 +
 +struct imx93_blk_ctrl_domain;
 +
 +struct imx93_blk_ctrl {
 +      struct device *dev;
 +      struct regmap *regmap;
 +      int num_clks;
 +      struct clk_bulk_data clks[BLK_MAX_CLKS];
 +      struct imx93_blk_ctrl_domain *domains;
 +      struct genpd_onecell_data onecell_data;
 +};
 +
 +#define DOMAIN_MAX_QOS 4
 +
 +struct imx93_blk_ctrl_qos {
 +      u32 reg;
 +      u32 cfg_off;
 +      u32 default_prio;
 +      u32 cfg_prio;
 +};
 +
 +struct imx93_blk_ctrl_domain_data {
 +      const char *name;
 +      const char * const *clk_names;
 +      int num_clks;
 +      u32 rst_mask;
 +      u32 clk_mask;
 +      int num_qos;
 +      struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS];
 +};
 +
 +struct imx93_blk_ctrl_domain {
 +      struct generic_pm_domain genpd;
 +      const struct imx93_blk_ctrl_domain_data *data;
 +      struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
 +      struct imx93_blk_ctrl *bc;
 +};
 +
 +struct imx93_blk_ctrl_data {
 +      const struct imx93_blk_ctrl_domain_data *domains;
 +      int num_domains;
 +      const char * const *clk_names;
 +      int num_clks;
 +      const struct regmap_access_table *reg_access_table;
 +};
 +
 +static inline struct imx93_blk_ctrl_domain *
 +to_imx93_blk_ctrl_domain(struct generic_pm_domain *genpd)
 +{
 +      return container_of(genpd, struct imx93_blk_ctrl_domain, genpd);
 +}
 +
 +static int imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain *domain)
 +{
 +      const struct imx93_blk_ctrl_domain_data *data = domain->data;
 +      struct imx93_blk_ctrl *bc = domain->bc;
 +      const struct imx93_blk_ctrl_qos *qos;
 +      u32 val, mask;
 +      int i;
 +
 +      for (i = 0; i < data->num_qos; i++) {
 +              qos = &data->qos[i];
 +
 +              mask = PRIO_MASK << qos->cfg_off;
 +              mask |= PRIO_MASK << (qos->cfg_off + 4);
 +              val = qos->cfg_prio << qos->cfg_off;
 +              val |= qos->default_prio << (qos->cfg_off + 4);
 +
 +              regmap_write_bits(bc->regmap, qos->reg, mask, val);
 +
 +              dev_dbg(bc->dev, "data->qos[i].reg 0x%x 0x%x\n", qos->reg, val);
 +      }
 +
 +      return 0;
 +}
 +
 +static int imx93_blk_ctrl_power_on(struct generic_pm_domain *genpd)
 +{
 +      struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd);
 +      const struct imx93_blk_ctrl_domain_data *data = domain->data;
 +      struct imx93_blk_ctrl *bc = domain->bc;
 +      int ret;
 +
 +      ret = clk_bulk_prepare_enable(bc->num_clks, bc->clks);
 +      if (ret) {
 +              dev_err(bc->dev, "failed to enable bus clocks\n");
 +              return ret;
 +      }
 +
 +      ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
 +      if (ret) {
 +              clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
 +              dev_err(bc->dev, "failed to enable clocks\n");
 +              return ret;
 +      }
 +
 +      ret = pm_runtime_get_sync(bc->dev);
 +      if (ret < 0) {
 +              pm_runtime_put_noidle(bc->dev);
 +              dev_err(bc->dev, "failed to power up domain\n");
 +              goto disable_clk;
 +      }
 +
 +      /* ungate clk */
 +      regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
 +
 +      /* release reset */
 +      regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
 +
 +      dev_dbg(bc->dev, "pd_on: name: %s\n", genpd->name);
 +
 +      return imx93_blk_ctrl_set_qos(domain);
 +
 +disable_clk:
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +
 +      clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
 +
 +      return ret;
 +}
 +
 +static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd)
 +{
 +      struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd);
 +      const struct imx93_blk_ctrl_domain_data *data = domain->data;
 +      struct imx93_blk_ctrl *bc = domain->bc;
 +
 +      dev_dbg(bc->dev, "pd_off: name: %s\n", genpd->name);
 +
 +      regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
 +      regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
 +
 +      pm_runtime_put(bc->dev);
 +
 +      clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 +
 +      clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
 +
 +      return 0;
 +}
 +
++static struct lock_class_key blk_ctrl_genpd_lock_class;
++
 +static int imx93_blk_ctrl_probe(struct platform_device *pdev)
 +{
 +      struct device *dev = &pdev->dev;
 +      const struct imx93_blk_ctrl_data *bc_data = of_device_get_match_data(dev);
 +      struct imx93_blk_ctrl *bc;
 +      void __iomem *base;
 +      int i, ret;
 +
 +      struct regmap_config regmap_config = {
 +              .reg_bits       = 32,
 +              .val_bits       = 32,
 +              .reg_stride     = 4,
 +              .rd_table       = bc_data->reg_access_table,
 +              .wr_table       = bc_data->reg_access_table,
 +              .max_register   = SZ_4K,
 +      };
 +
 +      bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
 +      if (!bc)
 +              return -ENOMEM;
 +
 +      bc->dev = dev;
 +
 +      base = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(base))
 +              return PTR_ERR(base);
 +
 +      bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
 +      if (IS_ERR(bc->regmap))
 +              return dev_err_probe(dev, PTR_ERR(bc->regmap),
 +                                   "failed to init regmap\n");
 +
 +      bc->domains = devm_kcalloc(dev, bc_data->num_domains,
 +                                 sizeof(struct imx93_blk_ctrl_domain),
 +                                 GFP_KERNEL);
 +      if (!bc->domains)
 +              return -ENOMEM;
 +
 +      bc->onecell_data.num_domains = bc_data->num_domains;
 +      bc->onecell_data.domains =
 +              devm_kcalloc(dev, bc_data->num_domains,
 +                           sizeof(struct generic_pm_domain *), GFP_KERNEL);
 +      if (!bc->onecell_data.domains)
 +              return -ENOMEM;
 +
 +      for (i = 0; i < bc_data->num_clks; i++)
 +              bc->clks[i].id = bc_data->clk_names[i];
 +      bc->num_clks = bc_data->num_clks;
 +
 +      ret = devm_clk_bulk_get(dev, bc->num_clks, bc->clks);
 +      if (ret) {
 +              dev_err_probe(dev, ret, "failed to get bus clock\n");
 +              return ret;
 +      }
 +
 +      for (i = 0; i < bc_data->num_domains; i++) {
 +              const struct imx93_blk_ctrl_domain_data *data = &bc_data->domains[i];
 +              struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
 +              int j;
 +
 +              domain->data = data;
 +
 +              for (j = 0; j < data->num_clks; j++)
 +                      domain->clks[j].id = data->clk_names[j];
 +
 +              ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
 +              if (ret) {
 +                      dev_err_probe(dev, ret, "failed to get clock\n");
 +                      goto cleanup_pds;
 +              }
 +
 +              domain->genpd.name = data->name;
 +              domain->genpd.power_on = imx93_blk_ctrl_power_on;
 +              domain->genpd.power_off = imx93_blk_ctrl_power_off;
 +              domain->bc = bc;
 +
 +              ret = pm_genpd_init(&domain->genpd, NULL, true);
 +              if (ret) {
 +                      dev_err_probe(dev, ret, "failed to init power domain\n");
 +                      goto cleanup_pds;
 +              }
 +
++              /*
++               * We use runtime PM to trigger power on/off of the upstream GPC
++               * domain, as a strict hierarchical parent/child power domain
++               * setup doesn't allow us to meet the sequencing requirements.
++               * This means we have nested locking of genpd locks, without the
++               * nesting being visible at the genpd level, so we need a
++               * separate lock class to make lockdep aware of the fact that
++               * this are separate domain locks that can be nested without a
++               * self-deadlock.
++               */
++              lockdep_set_class(&domain->genpd.mlock,
++                                &blk_ctrl_genpd_lock_class);
++
 +              bc->onecell_data.domains[i] = &domain->genpd;
 +      }
 +
 +      pm_runtime_enable(dev);
 +
 +      ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
 +      if (ret) {
 +              dev_err_probe(dev, ret, "failed to add power domain provider\n");
 +              goto cleanup_pds;
 +      }
 +
 +      dev_set_drvdata(dev, bc);
 +
 +      return 0;
 +
 +cleanup_pds:
 +      for (i--; i >= 0; i--)
 +              pm_genpd_remove(&bc->domains[i].genpd);
 +
 +      return ret;
 +}
 +
 +static int imx93_blk_ctrl_remove(struct platform_device *pdev)
 +{
 +      struct imx93_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
 +      int i;
 +
 +      of_genpd_del_provider(pdev->dev.of_node);
 +
 +      for (i = 0; bc->onecell_data.num_domains; i++) {
 +              struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
 +
 +              pm_genpd_remove(&domain->genpd);
 +      }
 +
 +      return 0;
 +}
 +
 +static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = {
 +      [IMX93_MEDIABLK_PD_MIPI_DSI] = {
 +              .name = "mediablk-mipi-dsi",
 +              .clk_names = (const char *[]){ "dsi" },
 +              .num_clks = 1,
 +              .rst_mask = BIT(11) | BIT(12),
 +              .clk_mask = BIT(11) | BIT(12),
 +      },
 +      [IMX93_MEDIABLK_PD_MIPI_CSI] = {
 +              .name = "mediablk-mipi-csi",
 +              .clk_names = (const char *[]){ "cam", "csi" },
 +              .num_clks = 2,
 +              .rst_mask = BIT(9) | BIT(10),
 +              .clk_mask = BIT(9) | BIT(10),
 +      },
 +      [IMX93_MEDIABLK_PD_PXP] = {
 +              .name = "mediablk-pxp",
 +              .clk_names = (const char *[]){ "pxp" },
 +              .num_clks = 1,
 +              .rst_mask = BIT(7) | BIT(8),
 +              .clk_mask = BIT(7) | BIT(8),
 +              .num_qos = 2,
 +              .qos = {
 +                      {
 +                              .reg = PXP_QOS_REG,
 +                              .cfg_off = PXP_R_CFG_QOS_OFF,
 +                              .default_prio = PRIO(3),
 +                              .cfg_prio = PRIO(6),
 +                      }, {
 +                              .reg = PXP_QOS_REG,
 +                              .cfg_off = PXP_W_CFG_QOS_OFF,
 +                              .default_prio = PRIO(3),
 +                              .cfg_prio = PRIO(6),
 +                      }
 +              }
 +      },
 +      [IMX93_MEDIABLK_PD_LCDIF] = {
 +              .name = "mediablk-lcdif",
 +              .clk_names = (const char *[]){ "disp", "lcdif" },
 +              .num_clks = 2,
 +              .rst_mask = BIT(4) | BIT(5) | BIT(6),
 +              .clk_mask = BIT(4) | BIT(5) | BIT(6),
 +              .num_qos = 1,
 +              .qos = {
 +                      {
 +                      .reg = LCDIF_QOS_REG,
 +                      .cfg_off = LCDIF_CFG_QOS_OFF,
 +                      .default_prio = PRIO(3),
 +                      .cfg_prio = PRIO(7),
 +                      }
 +              }
 +      },
 +      [IMX93_MEDIABLK_PD_ISI] = {
 +              .name = "mediablk-isi",
 +              .clk_names = (const char *[]){ "isi" },
 +              .num_clks = 1,
 +              .rst_mask = BIT(2) | BIT(3),
 +              .clk_mask = BIT(2) | BIT(3),
 +              .num_qos = 4,
 +              .qos = {
 +                      {
 +                              .reg = ISI_QOS_REG,
 +                              .cfg_off = ISI_Y_W_CFG_QOS_OFF,
 +                              .default_prio = PRIO(3),
 +                              .cfg_prio = PRIO(7),
 +                      }, {
 +                              .reg = ISI_QOS_REG,
 +                              .cfg_off = ISI_Y_R_CFG_QOS_OFF,
 +                              .default_prio = PRIO(3),
 +                              .cfg_prio = PRIO(7),
 +                      }, {
 +                              .reg = ISI_QOS_REG,
 +                              .cfg_off = ISI_U_CFG_QOS_OFF,
 +                              .default_prio = PRIO(3),
 +                              .cfg_prio = PRIO(7),
 +                      }, {
 +                              .reg = ISI_QOS_REG,
 +                              .cfg_off = ISI_V_CFG_QOS_OFF,
 +                              .default_prio = PRIO(3),
 +                              .cfg_prio = PRIO(7),
 +                      }
 +              }
 +      },
 +};
 +
 +static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = {
 +      regmap_reg_range(BLK_SFT_RSTN, BLK_CLK_EN),
 +      regmap_reg_range(LCDIF_QOS_REG, ISI_CACHE_REG),
 +      regmap_reg_range(ISI_QOS_REG, ISI_QOS_REG),
 +};
 +
 +static const struct regmap_access_table imx93_media_blk_ctl_access_table = {
 +      .yes_ranges = imx93_media_blk_ctl_yes_ranges,
 +      .n_yes_ranges = ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges),
 +};
 +
 +static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data = {
 +      .domains = imx93_media_blk_ctl_domain_data,
 +      .num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data),
 +      .clk_names = (const char *[]){ "axi", "apb", "nic", },
 +      .num_clks = 3,
 +      .reg_access_table = &imx93_media_blk_ctl_access_table,
 +};
 +
 +static const struct of_device_id imx93_blk_ctrl_of_match[] = {
 +      {
 +              .compatible = "fsl,imx93-media-blk-ctrl",
 +              .data = &imx93_media_blk_ctl_dev_data
 +      }, {
 +              /* Sentinel */
 +      }
 +};
 +MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match);
 +
 +static struct platform_driver imx93_blk_ctrl_driver = {
 +      .probe = imx93_blk_ctrl_probe,
 +      .remove = imx93_blk_ctrl_remove,
 +      .driver = {
 +              .name = "imx93-blk-ctrl",
 +              .of_match_table = imx93_blk_ctrl_of_match,
 +      },
 +};
 +module_platform_driver(imx93_blk_ctrl_driver);
 +
 +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
 +MODULE_DESCRIPTION("i.MX93 BLK CTRL driver");
 +MODULE_LICENSE("GPL");
index 832deeed8fd676a3dec5a16c79c54e42ba46ece4,0000000000000000000000000000000000000000..b9e60d136875ae3a8e13b9d9edea5c3e3e3f814b
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,176 @@@
- #include <linux/of_device.h>
 +// SPDX-License-Identifier: GPL-2.0
 +/*
 + * Copyright 2022 NXP
 + */
 +
 +#include <linux/clk.h>
 +#include <linux/delay.h>
 +#include <linux/iopoll.h>
++#include <linux/mod_devicetable.h>
 +#include <linux/module.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_domain.h>
 +
 +#define MIX_SLICE_SW_CTRL_OFF         0x20
 +#define SLICE_SW_CTRL_PSW_CTRL_OFF_MASK       BIT(4)
 +#define SLICE_SW_CTRL_PDN_SOFT_MASK   BIT(31)
 +
 +#define MIX_FUNC_STAT_OFF             0xB4
 +
 +#define FUNC_STAT_PSW_STAT_MASK               BIT(0)
 +#define FUNC_STAT_RST_STAT_MASK               BIT(2)
 +#define FUNC_STAT_ISO_STAT_MASK               BIT(4)
 +
 +struct imx93_power_domain {
 +      struct generic_pm_domain genpd;
 +      struct device *dev;
 +      void __iomem *addr;
 +      struct clk_bulk_data *clks;
 +      int num_clks;
 +      bool init_off;
 +};
 +
 +#define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd)
 +
 +static int imx93_pd_on(struct generic_pm_domain *genpd)
 +{
 +      struct imx93_power_domain *domain = to_imx93_pd(genpd);
 +      void __iomem *addr = domain->addr;
 +      u32 val;
 +      int ret;
 +
 +      ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
 +      if (ret) {
 +              dev_err(domain->dev, "failed to enable clocks for domain: %s\n", genpd->name);
 +              return ret;
 +      }
 +
 +      val = readl(addr + MIX_SLICE_SW_CTRL_OFF);
 +      val &= ~SLICE_SW_CTRL_PDN_SOFT_MASK;
 +      writel(val, addr + MIX_SLICE_SW_CTRL_OFF);
 +
 +      ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val,
 +                               !(val & FUNC_STAT_ISO_STAT_MASK), 1, 10000);
 +      if (ret) {
 +              dev_err(domain->dev, "pd_on timeout: name: %s, stat: %x\n", genpd->name, val);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static int imx93_pd_off(struct generic_pm_domain *genpd)
 +{
 +      struct imx93_power_domain *domain = to_imx93_pd(genpd);
 +      void __iomem *addr = domain->addr;
 +      int ret;
 +      u32 val;
 +
 +      /* Power off MIX */
 +      val = readl(addr + MIX_SLICE_SW_CTRL_OFF);
 +      val |= SLICE_SW_CTRL_PDN_SOFT_MASK;
 +      writel(val, addr + MIX_SLICE_SW_CTRL_OFF);
 +
 +      ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val,
 +                               val & FUNC_STAT_PSW_STAT_MASK, 1, 1000);
 +      if (ret) {
 +              dev_err(domain->dev, "pd_off timeout: name: %s, stat: %x\n", genpd->name, val);
 +              return ret;
 +      }
 +
 +      clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +
 +      return 0;
 +};
 +
 +static int imx93_pd_remove(struct platform_device *pdev)
 +{
 +      struct imx93_power_domain *domain = platform_get_drvdata(pdev);
 +      struct device *dev = &pdev->dev;
 +      struct device_node *np = dev->of_node;
 +
 +      if (!domain->init_off)
 +              clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +
 +      of_genpd_del_provider(np);
 +      pm_genpd_remove(&domain->genpd);
 +
 +      return 0;
 +}
 +
 +static int imx93_pd_probe(struct platform_device *pdev)
 +{
 +      struct device *dev = &pdev->dev;
 +      struct device_node *np = dev->of_node;
 +      struct imx93_power_domain *domain;
 +      int ret;
 +
 +      domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL);
 +      if (!domain)
 +              return -ENOMEM;
 +
 +      domain->addr = devm_platform_ioremap_resource(pdev, 0);
 +      if (IS_ERR(domain->addr))
 +              return PTR_ERR(domain->addr);
 +
 +      domain->num_clks = devm_clk_bulk_get_all(dev, &domain->clks);
 +      if (domain->num_clks < 0)
 +              return dev_err_probe(dev, domain->num_clks, "Failed to get domain's clocks\n");
 +
 +      domain->genpd.name = dev_name(dev);
 +      domain->genpd.power_off = imx93_pd_off;
 +      domain->genpd.power_on = imx93_pd_on;
 +      domain->dev = dev;
 +
 +      domain->init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK;
 +      /* Just to sync the status of hardware */
 +      if (!domain->init_off) {
 +              ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
 +              if (ret) {
 +                      dev_err(domain->dev, "failed to enable clocks for domain: %s\n",
 +                              domain->genpd.name);
 +                      return ret;
 +              }
 +      }
 +
 +      ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off);
 +      if (ret)
 +              goto err_clk_unprepare;
 +
 +      platform_set_drvdata(pdev, domain);
 +
 +      ret = of_genpd_add_provider_simple(np, &domain->genpd);
 +      if (ret)
 +              goto err_genpd_remove;
 +
 +      return 0;
 +
 +err_genpd_remove:
 +      pm_genpd_remove(&domain->genpd);
 +
 +err_clk_unprepare:
 +      if (!domain->init_off)
 +              clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 +
 +      return ret;
 +}
 +
 +static const struct of_device_id imx93_pd_ids[] = {
 +      { .compatible = "fsl,imx93-src-slice" },
 +      { }
 +};
 +MODULE_DEVICE_TABLE(of, imx93_pd_ids);
 +
 +static struct platform_driver imx93_power_domain_driver = {
 +      .driver = {
 +              .name   = "imx93_power_domain",
 +              .of_match_table = imx93_pd_ids,
 +      },
 +      .probe = imx93_pd_probe,
 +      .remove = imx93_pd_remove,
 +};
 +module_platform_driver(imx93_power_domain_driver);
 +
 +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
 +MODULE_DESCRIPTION("NXP i.MX93 power domain driver");
 +MODULE_LICENSE("GPL");