[U-Boot] [PATCH 0/2] amlogic: Add support for the Everything-Else power domain driver

For SM1 support a new Power Domain driver is needed to handle more than the VPU power domain.
This Power Domain driver is also used for G12A to also control the Ethernet Power Domain for example.
Neil Armstrong (2): power: domain: add Amlogic Everything-Else power domain driver configs: sei510: enable everything-else power domain
configs/sei510_defconfig | 2 +- drivers/power/domain/Kconfig | 7 + drivers/power/domain/Makefile | 1 + drivers/power/domain/meson-ee-pwrc.c | 430 +++++++++++++++++++++++++++ 4 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 drivers/power/domain/meson-ee-pwrc.c

Based on the 54ecb8f7028c ("Linux 5.4-rc1") Everything-Else power domain driver for Amlogic SoCs.
This driver handles the VPU Power domain and other domains from the Everything-Else part of the SM1 and G12A SoCs.
Signed-off-by: Neil Armstrong narmstrong@baylibre.com --- drivers/power/domain/Kconfig | 7 + drivers/power/domain/Makefile | 1 + drivers/power/domain/meson-ee-pwrc.c | 430 +++++++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 drivers/power/domain/meson-ee-pwrc.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index 06bba6220b..f5e428b3f5 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -37,6 +37,13 @@ config MESON_GX_VPU_POWER_DOMAIN Enable support for manipulating Amlogic Meson GX Video Processing Unit power domain.
+config MESON_EE_POWER_DOMAIN + bool "Enable Amlogic Everything-Else power domain driver" + depends on POWER_DOMAIN && ARCH_MESON + help + Enable support for manipulating Amlogic Meson Everything-Else power + domains. + config SANDBOX_POWER_DOMAIN bool "Enable the sandbox power domain test driver" depends on POWER_DOMAIN && SANDBOX diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 695aafe17d..91e4ca9d29 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o +obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o diff --git a/drivers/power/domain/meson-ee-pwrc.c b/drivers/power/domain/meson-ee-pwrc.c new file mode 100644 index 0000000000..21d4c9d4dd --- /dev/null +++ b/drivers/power/domain/meson-ee-pwrc.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong narmstrong@baylibre.com + */ + +#include <common.h> +#include <dm.h> +#include <power-domain-uclass.h> +#include <regmap.h> +#include <syscon.h> +#include <reset.h> +#include <clk.h> +#include <dt-bindings/power/meson-g12a-power.h> +#include <dt-bindings/power/meson-sm1-power.h> + +/* AO Offsets */ + +#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) +#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG3 (0x43 << 2) +#define HHI_VPU_MEM_PD_REG4 (0x44 << 2) +#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2) +#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2) +#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) + +struct meson_ee_pwrc; +struct meson_ee_pwrc_domain; + +struct meson_ee_pwrc_mem_domain { + unsigned int reg; + unsigned int mask; +}; + +struct meson_ee_pwrc_top_domain { + unsigned int sleep_reg; + unsigned int sleep_mask; + unsigned int iso_reg; + unsigned int iso_mask; +}; + +struct meson_ee_pwrc_domain_desc { + char *name; + unsigned int reset_names_count; + unsigned int clk_names_count; + struct meson_ee_pwrc_top_domain *top_pd; + unsigned int mem_pd_count; + struct meson_ee_pwrc_mem_domain *mem_pd; + bool (*get_power)(struct power_domain *power_domain); +}; + +struct meson_ee_pwrc_domain_data { + unsigned int count; + struct meson_ee_pwrc_domain_desc *domains; +}; + +/* TOP Power Domains */ + +static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = { + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +#define SM1_EE_PD(__bit) \ + { \ + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \ + .sleep_mask = BIT(__bit), \ + .iso_reg = AO_RTI_GEN_PWR_ISO0, \ + .iso_mask = BIT(__bit), \ + } + +static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); +static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); +static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); +static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); +static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); + +/* Memory PD Domains */ + +#define VPU_MEMPD(__reg) \ + { __reg, GENMASK(1, 0) }, \ + { __reg, GENMASK(3, 2) }, \ + { __reg, GENMASK(5, 4) }, \ + { __reg, GENMASK(7, 6) }, \ + { __reg, GENMASK(9, 8) }, \ + { __reg, GENMASK(11, 10) }, \ + { __reg, GENMASK(13, 12) }, \ + { __reg, GENMASK(15, 14) }, \ + { __reg, GENMASK(17, 16) }, \ + { __reg, GENMASK(19, 18) }, \ + { __reg, GENMASK(21, 20) }, \ + { __reg, GENMASK(23, 22) }, \ + { __reg, GENMASK(25, 24) }, \ + { __reg, GENMASK(27, 26) }, \ + { __reg, GENMASK(29, 28) }, \ + { __reg, GENMASK(31, 30) } + +#define VPU_HHI_MEMPD(__reg) \ + { __reg, BIT(8) }, \ + { __reg, BIT(9) }, \ + { __reg, BIT(10) }, \ + { __reg, BIT(11) }, \ + { __reg, BIT(12) }, \ + { __reg, BIT(13) }, \ + { __reg, BIT(14) }, \ + { __reg, BIT(15) } + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = { + { HHI_MEM_PD_REG0, GENMASK(3, 2) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_MEMPD(HHI_VPU_MEM_PD_REG3), + { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) }, + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { + { HHI_NANOQ_MEM_PD_REG0, 0xff }, + { HHI_NANOQ_MEM_PD_REG1, 0xff }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { + { HHI_MEM_PD_REG0, GENMASK(31, 30) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { + { HHI_MEM_PD_REG0, GENMASK(29, 26) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { + { HHI_MEM_PD_REG0, GENMASK(25, 18) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { + { HHI_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, +}; + +#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \ + { \ + .name = __name, \ + .reset_names_count = __resets, \ + .clk_names_count = __clks, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define TOP_PD(__name, __top_pd, __mem, __get_power) \ + { \ + .name = __name, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define MEM_PD(__name, __mem) \ + TOP_PD(__name, NULL, __mem, NULL) + +static bool pwrc_ee_get_power(struct power_domain *power_domain); + +static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { + [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { + [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, + pwrc_ee_get_power), + [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb, + pwrc_ee_get_power), + [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie, + pwrc_ee_get_power), + [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, + pwrc_ee_get_power), + [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), + [PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +struct meson_ee_pwrc_priv { + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct reset_ctl_bulk resets; + struct clk_bulk clks; + const struct meson_ee_pwrc_domain_data *data; +}; + +static bool pwrc_ee_get_power(struct power_domain *power_domain) +{ + struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev); + struct meson_ee_pwrc_domain_desc *pwrc_domain; + u32 reg; + + pwrc_domain = &priv->data->domains[power_domain->id]; + + regmap_read(priv->regmap_ao, + pwrc_domain->top_pd->sleep_reg, ®); + + return (reg & pwrc_domain->top_pd->sleep_mask); +} + +static int meson_ee_pwrc_request(struct power_domain *power_domain) +{ + return 0; +} + +static int meson_ee_pwrc_free(struct power_domain *power_domain) +{ + return 0; +} + +static int meson_ee_pwrc_off(struct power_domain *power_domain) +{ + struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev); + struct meson_ee_pwrc_domain_desc *pwrc_domain; + int i; + + pwrc_domain = &priv->data->domains[power_domain->id]; + + if (pwrc_domain->top_pd) + regmap_update_bits(priv->regmap_ao, + pwrc_domain->top_pd->sleep_reg, + pwrc_domain->top_pd->sleep_mask, + pwrc_domain->top_pd->sleep_mask); + udelay(20); + + for (i = 0 ; i < pwrc_domain->mem_pd_count ; ++i) + regmap_update_bits(priv->regmap_hhi, + pwrc_domain->mem_pd[i].reg, + pwrc_domain->mem_pd[i].mask, + pwrc_domain->mem_pd[i].mask); + + udelay(20); + + if (pwrc_domain->top_pd) + regmap_update_bits(priv->regmap_ao, + pwrc_domain->top_pd->iso_reg, + pwrc_domain->top_pd->iso_mask, + pwrc_domain->top_pd->iso_mask); + + if (pwrc_domain->clk_names_count) { + mdelay(20); + clk_disable_bulk(&priv->clks); + } + + return 0; +} + +static int meson_ee_pwrc_on(struct power_domain *power_domain) +{ + struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev); + struct meson_ee_pwrc_domain_desc *pwrc_domain; + int i, ret; + + pwrc_domain = &priv->data->domains[power_domain->id]; + + if (pwrc_domain->top_pd) + regmap_update_bits(priv->regmap_ao, + pwrc_domain->top_pd->sleep_reg, + pwrc_domain->top_pd->sleep_mask, 0); + udelay(20); + + for (i = 0 ; i < pwrc_domain->mem_pd_count ; ++i) + regmap_update_bits(priv->regmap_hhi, + pwrc_domain->mem_pd[i].reg, + pwrc_domain->mem_pd[i].mask, 0); + + udelay(20); + + if (pwrc_domain->reset_names_count) { + ret = reset_assert_bulk(&priv->resets); + if (ret) + return ret; + } + + if (pwrc_domain->top_pd) + regmap_update_bits(priv->regmap_ao, + pwrc_domain->top_pd->iso_reg, + pwrc_domain->top_pd->iso_mask, 0); + + if (pwrc_domain->reset_names_count) { + ret = reset_deassert_bulk(&priv->resets); + if (ret) + return ret; + } + + if (pwrc_domain->clk_names_count) + return clk_enable_bulk(&priv->clks); + + return 0; +} + +static int meson_ee_pwrc_of_xlate(struct power_domain *power_domain, + struct ofnode_phandle_args *args) +{ + struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev); + + /* #power-domain-cells is 1 */ + + if (args->args_count < 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + power_domain->id = args->args[0]; + + if (power_domain->id >= priv->data->count) { + debug("Invalid domain ID: %lu\n", power_domain->id); + return -EINVAL; + } + + return 0; +} + +struct power_domain_ops meson_ee_pwrc_ops = { + .free = meson_ee_pwrc_free, + .off = meson_ee_pwrc_off, + .on = meson_ee_pwrc_on, + .request = meson_ee_pwrc_request, + .of_xlate = meson_ee_pwrc_of_xlate, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { + .count = ARRAY_SIZE(g12a_pwrc_domains), + .domains = g12a_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { + .count = ARRAY_SIZE(sm1_pwrc_domains), + .domains = sm1_pwrc_domains, +}; + +static const struct udevice_id meson_ee_pwrc_ids[] = { + { + .compatible = "amlogic,meson-g12a-pwrc", + .data = (unsigned long)&meson_ee_g12a_pwrc_data, + }, + { + .compatible = "amlogic,meson-sm1-pwrc", + .data = (unsigned long)&meson_ee_sm1_pwrc_data, + }, + { } +}; + +static int meson_ee_pwrc_probe(struct udevice *dev) +{ + struct meson_ee_pwrc_priv *priv = dev_get_priv(dev); + u32 ao_phandle; + ofnode ao_node; + int ret; + + priv->data = (void *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + priv->regmap_hhi = syscon_node_to_regmap(dev_get_parent(dev)->node); + if (IS_ERR(priv->regmap_hhi)) + return PTR_ERR(priv->regmap_hhi); + + ret = ofnode_read_u32(dev->node, "amlogic,ao-sysctrl", + &ao_phandle); + if (ret) + return ret; + + ao_node = ofnode_get_by_phandle(ao_phandle); + if (!ofnode_valid(ao_node)) + return -EINVAL; + + priv->regmap_ao = syscon_node_to_regmap(ao_node); + if (IS_ERR(priv->regmap_ao)) + return PTR_ERR(priv->regmap_ao); + + ret = reset_get_bulk(dev, &priv->resets); + if (ret) + return ret; + + ret = clk_get_bulk(dev, &priv->clks); + if (ret) + return ret; + + return 0; +} + +U_BOOT_DRIVER(meson_ee_pwrc) = { + .name = "meson_ee_pwrc", + .id = UCLASS_POWER_DOMAIN, + .of_match = meson_ee_pwrc_ids, + .probe = meson_ee_pwrc_probe, + .ops = &meson_ee_pwrc_ops, + .priv_auto_alloc_size = sizeof(struct meson_ee_pwrc_priv), +};

The GX VPU Power Domain driver has been replaced by the Everything-Else Power Domain driver for G12A and SM1.
Signed-off-by: Neil Armstrong narmstrong@baylibre.com --- configs/sei510_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configs/sei510_defconfig b/configs/sei510_defconfig index 9eb205bc63..9df04c345d 100644 --- a/configs/sei510_defconfig +++ b/configs/sei510_defconfig @@ -48,7 +48,7 @@ CONFIG_MESON_G12A_USB_PHY=y CONFIG_PINCTRL=y CONFIG_PINCTRL_MESON_G12A=y CONFIG_POWER_DOMAIN=y -CONFIG_MESON_GX_VPU_POWER_DOMAIN=y +CONFIG_MESON_EE_POWER_DOMAIN=y CONFIG_DM_VIDEO=y CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_VIDEO_MESON=y

On 11/10/2019 15:12, Neil Armstrong wrote:
For SM1 support a new Power Domain driver is needed to handle more than the VPU power domain.
This Power Domain driver is also used for G12A to also control the Ethernet Power Domain for example.
Neil Armstrong (2): power: domain: add Amlogic Everything-Else power domain driver configs: sei510: enable everything-else power domain
configs/sei510_defconfig | 2 +- drivers/power/domain/Kconfig | 7 + drivers/power/domain/Makefile | 1 + drivers/power/domain/meson-ee-pwrc.c | 430 +++++++++++++++++++++++++++ 4 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 drivers/power/domain/meson-ee-pwrc.c
Applied to u-boot-amlogic
participants (1)
-
Neil Armstrong