On Wed, Mar 30, 2022 at 8:04 PM Marek Vasut <ma...@denx.de> wrote: > > This driver is the only SMCCC dependency in iMX8M U-Boot port. Rework > the driver based on Linux GPCv2 driver to directly control the GPCv2 > block instead of using SMCCC calls. This way, U-Boot can operate the > i.MX8M power domains without depending on anything else. > > This is losely based on Linux GPCv2 driver. The GPU, VPU, MIPI power > domains are not supported to save space, since they are not useful in > the bootloader. The only domains kept are ones for HSIO, PCIe, USB. > > Signed-off-by: Marek Vasut <ma...@denx.de> > Cc: Fabio Estevam <feste...@gmail.com> > Cc: Peng Fan <peng....@nxp.com> > Cc: Stefano Babic <sba...@denx.de> > --- > NOTE: So far this is tested on MX8MM and MX8MN. MX8MQ is not tested. > --- > drivers/power/domain/Kconfig | 1 + > drivers/power/domain/imx8m-power-domain.c | 379 ++++++++++++++++++++-- > 2 files changed, 361 insertions(+), 19 deletions(-) > > diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig > index 93d2599d83c..04fc0054323 100644 > --- a/drivers/power/domain/Kconfig > +++ b/drivers/power/domain/Kconfig > @@ -35,6 +35,7 @@ config IMX8_POWER_DOMAIN > config IMX8M_POWER_DOMAIN > bool "Enable i.MX8M power domain driver" > depends on POWER_DOMAIN && ARCH_IMX8M > + select CLK > help > Enable support for manipulating NXP i.MX8M on-SoC power domains via > requests to the ATF. > diff --git a/drivers/power/domain/imx8m-power-domain.c > b/drivers/power/domain/imx8m-power-domain.c > index c32dbcc31ae..e2e41cf5fee 100644 > --- a/drivers/power/domain/imx8m-power-domain.c > +++ b/drivers/power/domain/imx8m-power-domain.c > @@ -4,6 +4,7 @@ > */ > > #include <common.h> > +#include <clk.h> > #include <dm.h> > #include <malloc.h> > #include <power-domain-uclass.h> > @@ -12,52 +13,361 @@ > #include <asm/mach-imx/sys_proto.h> > #include <dm/device-internal.h> > #include <dm/device.h> > +#include <dm/device_compat.h> > #include <imx_sip.h> > -#include <linux/arm-smccc.h> > +#include <linux/bitmap.h> > +#include <wait_bit.h> > + > +#include <dt-bindings/power/imx8mm-power.h> > +#include <dt-bindings/power/imx8mn-power.h> > +#include <dt-bindings/power/imx8mq-power.h> > > DECLARE_GLOBAL_DATA_PTR; > > +#define GPC_PGC_CPU_MAPPING 0x0ec > + > +#define IMX8M_PCIE2_A53_DOMAIN BIT(15) > +#define IMX8M_OTG2_A53_DOMAIN BIT(5) > +#define IMX8M_OTG1_A53_DOMAIN BIT(4) > +#define IMX8M_PCIE1_A53_DOMAIN BIT(3) > + > +#define IMX8MM_OTG2_A53_DOMAIN BIT(5) > +#define IMX8MM_OTG1_A53_DOMAIN BIT(4) > +#define IMX8MM_PCIE_A53_DOMAIN BIT(3) > + > +#define IMX8MN_OTG1_A53_DOMAIN BIT(4) > +#define IMX8MN_MIPI_A53_DOMAIN BIT(2) > + > +#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 > +#define GPC_PU_PGC_SW_PDN_REQ 0x104 > + > +#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) > +#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 IMX8MM_OTG2_SW_Pxx_REQ BIT(3) > +#define IMX8MM_OTG1_SW_Pxx_REQ BIT(2) > +#define IMX8MM_PCIE_SW_Pxx_REQ BIT(1) > + > +#define IMX8MN_OTG1_SW_Pxx_REQ BIT(2) > +#define IMX8MN_MIPI_SW_Pxx_REQ BIT(0) > + > +#define GPC_M4_PU_PDN_FLG 0x1bc > + > +#define GPC_PU_PWRHSK 0x1fc > + > +#define IMX8MM_HSIO_HSK_PWRDNACKN (BIT(23) | BIT(24)) > +#define IMX8MM_HSIO_HSK_PWRDNREQN (BIT(5) | BIT(6)) > + > +#define IMX8MN_HSIO_HSK_PWRDNACKN BIT(23) > +#define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5) > + > +/* > + * 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 IMX8M_PGC_PCIE1 17 > +#define IMX8M_PGC_OTG1 18 > +#define IMX8M_PGC_OTG2 19 > +#define IMX8M_PGC_PCIE2 29 > + > +#define IMX8MM_PGC_PCIE 17 > +#define IMX8MM_PGC_OTG1 18 > +#define IMX8MM_PGC_OTG2 19 > + > +#define IMX8MN_PGC_OTG1 18 > + > +#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 { > + unsigned long pgc; > + > + const struct { > + u32 pxx; > + u32 map; > + u32 hskreq; > + u32 hskack; > + } bits; > + > + const bool keep_clocks; > +}; > + > +struct imx_pgc_domain_data { > + const struct imx_pgc_domain *domains; > + size_t domains_num; > + const struct imx_pgc_regs *pgc_regs; > +}; > + > struct imx8m_power_domain_plat { > + struct power_domain pd; > + const struct imx_pgc_domain *domain; > + const struct imx_pgc_regs *regs; > + struct clk_bulk clk; > + void __iomem *base; > int resource_id; > int has_pd; > - struct power_domain pd; > }; > > +#if defined(CONFIG_IMX8MM) || defined(CONFIG_IMX8MN) || > defined(CONFIG_IMX8MQ) > +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, > +}; > +#endif > + > +#ifdef CONFIG_IMX8MQ > +static const struct imx_pgc_domain imx8m_pgc_domains[] = { > + [IMX8M_POWER_DOMAIN_PCIE1] = { > + .bits = { > + .pxx = IMX8M_PCIE1_SW_Pxx_REQ, > + .map = IMX8M_PCIE1_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8M_PGC_PCIE1), > + }, > + > + [IMX8M_POWER_DOMAIN_USB_OTG1] = { > + .bits = { > + .pxx = IMX8M_OTG1_SW_Pxx_REQ, > + .map = IMX8M_OTG1_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8M_PGC_OTG1), > + }, > + > + [IMX8M_POWER_DOMAIN_USB_OTG2] = { > + .bits = { > + .pxx = IMX8M_OTG2_SW_Pxx_REQ, > + .map = IMX8M_OTG2_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8M_PGC_OTG2), > + }, > + > + [IMX8M_POWER_DOMAIN_PCIE2] = { > + .bits = { > + .pxx = IMX8M_PCIE2_SW_Pxx_REQ, > + .map = IMX8M_PCIE2_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8M_PGC_PCIE2), > + }, > +}; > + > +static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { > + .domains = imx8m_pgc_domains, > + .domains_num = ARRAY_SIZE(imx8m_pgc_domains), > + .pgc_regs = &imx7_pgc_regs, > +}; > +#endif > + > +#ifdef CONFIG_IMX8MM > +static const struct imx_pgc_domain imx8mm_pgc_domains[] = { > + [IMX8MM_POWER_DOMAIN_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] = { > + .bits = { > + .pxx = IMX8MM_PCIE_SW_Pxx_REQ, > + .map = IMX8MM_PCIE_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8MM_PGC_PCIE), > + }, > + > + [IMX8MM_POWER_DOMAIN_OTG1] = { > + .bits = { > + .pxx = IMX8MM_OTG1_SW_Pxx_REQ, > + .map = IMX8MM_OTG1_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8MM_PGC_OTG1), > + }, > + > + [IMX8MM_POWER_DOMAIN_OTG2] = { > + .bits = { > + .pxx = IMX8MM_OTG2_SW_Pxx_REQ, > + .map = IMX8MM_OTG2_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8MM_PGC_OTG2), > + }, > +}; > + > +static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = { > + .domains = imx8mm_pgc_domains, > + .domains_num = ARRAY_SIZE(imx8mm_pgc_domains), > + .pgc_regs = &imx7_pgc_regs, > +}; > +#endif > + > +#ifdef CONFIG_IMX8MN > +static const struct imx_pgc_domain imx8mn_pgc_domains[] = { > + [IMX8MN_POWER_DOMAIN_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] = { > + .bits = { > + .pxx = IMX8MN_OTG1_SW_Pxx_REQ, > + .map = IMX8MN_OTG1_A53_DOMAIN, > + }, > + .pgc = BIT(IMX8MN_PGC_OTG1), > + }, > +}; > + > +static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = { > + .domains = imx8mn_pgc_domains, > + .domains_num = ARRAY_SIZE(imx8mn_pgc_domains), > + .pgc_regs = &imx7_pgc_regs, > +}; > +#endif > + > static int imx8m_power_domain_on(struct power_domain *power_domain) > { > struct udevice *dev = power_domain->dev; > - struct imx8m_power_domain_plat *pdata; > + struct imx8m_power_domain_plat *pdata = dev_get_plat(dev); > + const struct imx_pgc_domain *domain = pdata->domain; > + const struct imx_pgc_regs *regs = pdata->regs; > + void __iomem *base = pdata->base; > + u32 pgc; > + int ret; > + > + if (pdata->clk.count) { > + ret = clk_enable_bulk(&pdata->clk); > + if (ret) { > + dev_err(dev, "failed to enable reset clocks\n"); > + return ret; > + } > + } > > - pdata = dev_get_plat(dev); > + if (domain->bits.pxx) { > + /* request the domain to power up */ > + setbits_le32(base + regs->pup, domain->bits.pxx); > > - if (pdata->resource_id < 0) > - return -EINVAL; > + /* > + * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait > + * for PUP_REQ/PDN_REQ bit to be cleared > + */ > + ret = wait_for_bit_le32(base + regs->pup, domain->bits.pxx, > + false, 1000, false); > + if (ret) { > + dev_err(dev, "failed to command PGC\n"); > + goto out_clk_disable; > + } > > - if (pdata->has_pd) > - power_domain_on(&pdata->pd); > + /* disable power control */ > + for_each_set_bit(pgc, &domain->pgc, 32) { > + clrbits_le32(base + GPC_PGC_CTRL(pgc), > + GPC_PGC_CTRL_PCR); > + } > + } > + > + /* delay for reset to propagate */ > + udelay(5); > > - arm_smccc_smc(IMX_SIP_GPC, IMX_SIP_GPC_PM_DOMAIN, > - pdata->resource_id, 1, 0, 0, 0, 0, NULL); > + /* request the ADB400 to power up */ > + if (domain->bits.hskreq) > + setbits_le32(base + regs->hsk, domain->bits.hskreq); > + > + /* Disable reset clocks for all devices in the domain */ > + if (!domain->keep_clocks && pdata->clk.count) > + clk_disable_bulk(&pdata->clk); > > return 0; > + > +out_clk_disable: > + if (pdata->clk.count) > + clk_disable_bulk(&pdata->clk); > + return ret; > } > > static int imx8m_power_domain_off(struct power_domain *power_domain) > { > struct udevice *dev = power_domain->dev; > - struct imx8m_power_domain_plat *pdata; > - pdata = dev_get_plat(dev); > + struct imx8m_power_domain_plat *pdata = dev_get_plat(dev); > + const struct imx_pgc_domain *domain = pdata->domain; > + const struct imx_pgc_regs *regs = pdata->regs; > + void __iomem *base = pdata->base; > + u32 pgc; > + int ret; > > - if (pdata->resource_id < 0) > - return -EINVAL; > + /* Enable reset clocks for all devices in the domain */ > + if (!domain->keep_clocks && pdata->clk.count) { > + ret = clk_enable_bulk(&pdata->clk); > + if (ret) > + return ret; > + } > > - arm_smccc_smc(IMX_SIP_GPC, IMX_SIP_GPC_PM_DOMAIN, > - pdata->resource_id, 0, 0, 0, 0, 0, NULL); > + /* request the ADB400 to power down */ > + if (domain->bits.hskreq) { > + clrbits_le32(base + regs->hsk, domain->bits.hskreq); > + > + ret = wait_for_bit_le32(base + regs->hsk, domain->bits.hskack, > + false, 1000, false); > + if (ret) { > + dev_err(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) { > + setbits_le32(base + GPC_PGC_CTRL(pgc), > + GPC_PGC_CTRL_PCR); > + } > + > + /* request the domain to power down */ > + setbits_le32(base + regs->pdn, 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 = wait_for_bit_le32(base + regs->pdn, domain->bits.pxx, > + false, 1000, false); > + if (ret) { > + dev_err(dev, "failed to command PGC\n"); > + goto out_clk_disable; > + } > + } > + > + /* Disable reset clocks for all devices in the domain */ > + if (pdata->clk.count) > + clk_disable_bulk(&pdata->clk); > > if (pdata->has_pd) > power_domain_off(&pdata->pd); > > return 0; > + > +out_clk_disable: > + if (!domain->keep_clocks && pdata->clk.count) > + clk_disable_bulk(&pdata->clk); > + > + return ret; > } > > static int imx8m_power_domain_of_xlate(struct power_domain *power_domain, > @@ -101,12 +411,36 @@ static int imx8m_power_domain_bind(struct udevice *dev) > return 0; > } > > +static int imx8m_power_domain_probe(struct udevice *dev) > +{ > + struct imx8m_power_domain_plat *pdata = dev_get_plat(dev); > + int ret; > + > + /* Nothing to do for non-"power-domain" driver instances. */ > + if (!strstr(dev->name, "power-domain")) > + return 0; > + > + /* Grab optional power domain clock. */ > + ret = clk_get_bulk(dev, &pdata->clk); > + if (ret && ret != -ENOENT) { > + dev_err(dev, "Failed to get domain clock (%d)\n", ret); > + return ret; > + } > + > + return 0; > +} > + > static int imx8m_power_domain_of_to_plat(struct udevice *dev) > { > struct imx8m_power_domain_plat *pdata = dev_get_plat(dev); > + struct imx_pgc_domain_data *domain_data = > + (struct imx_pgc_domain_data *)dev_get_driver_data(dev); > > pdata->resource_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), > "reg", -1); > + pdata->domain = &domain_data->domains[pdata->resource_id]; > + pdata->regs = domain_data->pgc_regs; > + pdata->base = dev_read_addr_ptr(dev->parent); > > if (!power_domain_get(dev, &pdata->pd)) > pdata->has_pd = 1; > @@ -115,9 +449,15 @@ static int imx8m_power_domain_of_to_plat(struct udevice > *dev) > } > > static const struct udevice_id imx8m_power_domain_ids[] = { > - { .compatible = "fsl,imx8mq-gpc" }, > - { .compatible = "fsl,imx8mm-gpc" }, > - { .compatible = "fsl,imx8mn-gpc" }, > +#ifdef CONFIG_IMX8MQ > + { .compatible = "fsl,imx8mq-gpc", .data = > (long)&imx8m_pgc_domain_data }, > +#endif > +#ifdef CONFIG_IMX8MM > + { .compatible = "fsl,imx8mm-gpc", .data = > (long)&imx8mm_pgc_domain_data }, > +#endif > +#ifdef CONFIG_IMX8MN > + { .compatible = "fsl,imx8mn-gpc", .data = > (long)&imx8mn_pgc_domain_data }, > +#endif > { } > }; > > @@ -132,6 +472,7 @@ U_BOOT_DRIVER(imx8m_power_domain) = { > .id = UCLASS_POWER_DOMAIN, > .of_match = imx8m_power_domain_ids, > .bind = imx8m_power_domain_bind, > + .probe = imx8m_power_domain_probe, > .of_to_plat = imx8m_power_domain_of_to_plat, > .plat_auto = sizeof(struct imx8m_power_domain_plat), > .ops = &imx8m_power_domain_ops, > -- > 2.35.1 >
Marek, I was able to test this without any issue on IMX8MM/IMX8MN/IMX8MP. I tested with NXP ATF lf_v2.4. Tested-By: Tim Harvey <thar...@gateworks.com> #imx8mm-venice-gw73xx-0x #imx8mn-venice-gw7902 #imx8mp-venice-gw74xx Best Regards, Tim