From bd0faf08dc7fcb3e555b35f556c9012b3c93de3b Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 14 Feb 2017 16:41:57 -0800 Subject: soc: bcm: brcmstb: Match additional compatible strings Match all known sun-top-ctrl compatible strings from our MIPS chips counterparts. This allows us to properly report the SoC information to user-space through our SoC driver. Signed-off-by: Florian Fainelli --- drivers/soc/bcm/brcmstb/common.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c index 94e7335553f4..b6195fdf0d00 100644 --- a/drivers/soc/bcm/brcmstb/common.c +++ b/drivers/soc/bcm/brcmstb/common.c @@ -41,6 +41,15 @@ bool soc_is_brcmstb(void) } static const struct of_device_id sun_top_ctrl_match[] = { + { .compatible = "brcm,bcm7125-sun-top-ctrl", }, + { .compatible = "brcm,bcm7346-sun-top-ctrl", }, + { .compatible = "brcm,bcm7358-sun-top-ctrl", }, + { .compatible = "brcm,bcm7360-sun-top-ctrl", }, + { .compatible = "brcm,bcm7362-sun-top-ctrl", }, + { .compatible = "brcm,bcm7420-sun-top-ctrl", }, + { .compatible = "brcm,bcm7425-sun-top-ctrl", }, + { .compatible = "brcm,bcm7429-sun-top-ctrl", }, + { .compatible = "brcm,bcm7425-sun-top-ctrl", }, { .compatible = "brcm,brcmstb-sun-top-ctrl", }, { } }; -- cgit v1.2.3 From 721cabf6c6600dbe689ee2782bc087270e97e652 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 17 Feb 2017 20:02:44 +0100 Subject: soc: imx: move PGC handling to a new GPC driver This is an almost complete re-write of the previous GPC power gating control code found in the IMX architecture code. It supports both the old and the new DT binding, allowing more domains to be added later and generally makes the driver easier to extend, while keeping compatibility with existing DTBs. As the result, all functionality regarding the power gating controller gets removed from the IMX architecture GPC driver. It keeps only the IRQ controller code in the architecture, as this is closely coupled to the CPU idle implementation. Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/Makefile | 1 + drivers/soc/imx/Makefile | 1 + drivers/soc/imx/gpc.c | 487 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 489 insertions(+) create mode 100644 drivers/soc/imx/Makefile create mode 100644 drivers/soc/imx/gpc.c (limited to 'drivers/soc') diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 05eae52a30b4..98fc73358da1 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -6,6 +6,7 @@ obj-y += bcm/ obj-$(CONFIG_ARCH_DOVE) += dove/ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ +obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-$(CONFIG_ARCH_RENESAS) += renesas/ diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile new file mode 100644 index 000000000000..35861f5b2802 --- /dev/null +++ b/drivers/soc/imx/Makefile @@ -0,0 +1 @@ +obj-y += gpc.o diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c new file mode 100644 index 000000000000..1e9b3b81f466 --- /dev/null +++ b/drivers/soc/imx/gpc.c @@ -0,0 +1,487 @@ +/* + * Copyright 2015-2017 Pengutronix, Lucas Stach + * Copyright 2011-2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPC_CNTR 0x000 + +#define GPC_PGC_PDN_OFFS 0x0 +#define GPC_PGC_PUPSCR_OFFS 0x4 +#define GPC_PGC_PDNSCR_OFFS 0x8 +#define GPC_PGC_SW2ISO_SHIFT 0x8 +#define GPC_PGC_SW_SHIFT 0x0 + +#define GPC_PGC_GPU_PDN 0x260 +#define GPC_PGC_GPU_PUPSCR 0x264 +#define GPC_PGC_GPU_PDNSCR 0x268 + +#define GPU_VPU_PUP_REQ BIT(1) +#define GPU_VPU_PDN_REQ BIT(0) + +#define GPC_CLK_MAX 6 + +struct imx_pm_domain { + struct generic_pm_domain base; + struct regmap *regmap; + struct regulator *supply; + struct clk *clk[GPC_CLK_MAX]; + int num_clks; + unsigned int reg_offs; + signed char cntr_pdn_bit; + unsigned int ipg_rate_mhz; +}; + +static inline struct imx_pm_domain * +to_imx_pm_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx_pm_domain, base); +} + +static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd) +{ + struct imx_pm_domain *pd = to_imx_pm_domain(genpd); + int iso, iso2sw; + u32 val; + + /* Read ISO and ISO2SW power down delays */ + regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val); + iso = val & 0x3f; + iso2sw = (val >> 8) & 0x3f; + + /* Gate off domain when powered down */ + regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_PDN_OFFS, + 0x1, 0x1); + + /* Request GPC to power down domain */ + val = BIT(pd->cntr_pdn_bit); + regmap_update_bits(pd->regmap, GPC_CNTR, val, val); + + /* Wait ISO + ISO2SW IPG clock cycles */ + udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz)); + + if (pd->supply) + regulator_disable(pd->supply); + + return 0; +} + +static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) +{ + struct imx_pm_domain *pd = to_imx_pm_domain(genpd); + int i, ret, sw, sw2iso; + u32 val; + + if (pd->supply) { + ret = regulator_enable(pd->supply); + if (ret) { + pr_err("%s: failed to enable regulator: %d\n", + __func__, ret); + return ret; + } + } + + /* Enable reset clocks for all devices in the domain */ + for (i = 0; i < pd->num_clks; i++) + clk_prepare_enable(pd->clk[i]); + + /* Gate off domain when powered down */ + regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_PDN_OFFS, + 0x1, 0x1); + + /* Read ISO and ISO2SW power down delays */ + regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val); + sw = val & 0x3f; + sw2iso = (val >> 8) & 0x3f; + + /* Request GPC to power up domain */ + val = BIT(pd->cntr_pdn_bit + 1); + regmap_update_bits(pd->regmap, GPC_CNTR, val, val); + + /* Wait ISO + ISO2SW IPG clock cycles */ + udelay(DIV_ROUND_UP(sw + sw2iso, pd->ipg_rate_mhz)); + + /* Disable reset clocks for all devices in the domain */ + for (i = 0; i < pd->num_clks; i++) + clk_disable_unprepare(pd->clk[i]); + + return 0; +} + +static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain) +{ + int i, ret; + + for (i = 0; ; i++) { + struct clk *clk = of_clk_get(dev->of_node, i); + if (IS_ERR(clk)) + break; + if (i >= GPC_CLK_MAX) { + dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX); + ret = -EINVAL; + goto clk_err; + } + domain->clk[i] = clk; + } + domain->num_clks = i; + + return 0; + +clk_err: + for (; i >= 0; i--) + clk_put(domain->clk[i]); + + return ret; +} + +static void imx_pgc_put_clocks(struct imx_pm_domain *domain) +{ + int i; + + for (i = domain->num_clks - 1; i >= 0; i--) + clk_put(domain->clk[i]); +} + +static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain) +{ + /* try to get the domain supply regulator */ + domain->supply = devm_regulator_get_optional(dev, "power"); + if (IS_ERR(domain->supply)) { + if (PTR_ERR(domain->supply) == -ENODEV) + domain->supply = NULL; + else + return PTR_ERR(domain->supply); + } + + /* try to get all clocks needed for reset propagation */ + return imx_pgc_get_clocks(dev, domain); +} + +static int imx_pgc_power_domain_probe(struct platform_device *pdev) +{ + struct imx_pm_domain *domain = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + int ret; + + /* if this PD is associated with a DT node try to parse it */ + if (dev->of_node) { + ret = imx_pgc_parse_dt(dev, domain); + if (ret) + return ret; + } + + /* initially power on the domain */ + if (domain->base.power_on) + domain->base.power_on(&domain->base); + + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { + pm_genpd_init(&domain->base, NULL, false); + ret = of_genpd_add_provider_simple(dev->of_node, &domain->base); + if (ret) + goto genpd_err; + } + + device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE); + + return 0; + +genpd_err: + pm_genpd_remove(&domain->base); + imx_pgc_put_clocks(domain); + + return ret; +} + +static int imx_pgc_power_domain_remove(struct platform_device *pdev) +{ + struct imx_pm_domain *domain = pdev->dev.platform_data; + + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { + of_genpd_del_provider(pdev->dev.of_node); + pm_genpd_remove(&domain->base); + imx_pgc_put_clocks(domain); + } + + return 0; +} + +static const struct platform_device_id imx_pgc_power_domain_id[] = { + { "imx-pgc-power-domain"}, + { }, +}; + +static struct platform_driver imx_pgc_power_domain_driver = { + .driver = { + .name = "imx-pgc-pd", + }, + .probe = imx_pgc_power_domain_probe, + .remove = imx_pgc_power_domain_remove, + .id_table = imx_pgc_power_domain_id, +}; +builtin_platform_driver(imx_pgc_power_domain_driver) + +static struct genpd_power_state imx6_pm_domain_pu_state = { + .power_off_latency_ns = 25000, + .power_on_latency_ns = 2000000, +}; + +static struct imx_pm_domain imx_gpc_domains[] = { + { + .base = { + .name = "ARM", + }, + }, { + .base = { + .name = "PU", + .power_off = imx6_pm_domain_power_off, + .power_on = imx6_pm_domain_power_on, + .states = &imx6_pm_domain_pu_state, + .state_count = 1, + }, + .reg_offs = 0x260, + .cntr_pdn_bit = 0, + }, { + .base = { + .name = "DISPLAY", + .power_off = imx6_pm_domain_power_off, + .power_on = imx6_pm_domain_power_on, + }, + .reg_offs = 0x240, + .cntr_pdn_bit = 4, + } +}; + +struct imx_gpc_dt_data { + int num_domains; +}; + +static const struct imx_gpc_dt_data imx6q_dt_data = { + .num_domains = 2, +}; + +static const struct imx_gpc_dt_data imx6sl_dt_data = { + .num_domains = 3, +}; + +static const struct of_device_id imx_gpc_dt_ids[] = { + { .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data }, + { .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data }, + { } +}; + +static bool imx_gpc_readable_reg(struct device *dev, unsigned int reg) +{ + return (reg % 4 == 0) && (reg <= 0x2ac); +} + +static bool imx_gpc_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == GPC_CNTR) + return true; + + return false; +} + +static const struct regmap_config imx_gpc_regmap_config = { + .cache_type = REGCACHE_FLAT, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + + .readable_reg = imx_gpc_readable_reg, + .volatile_reg = imx_gpc_volatile_reg, + + .max_register = 0x2ac, +}; + +static struct generic_pm_domain *imx_gpc_onecell_domains[] = { + &imx_gpc_domains[0].base, + &imx_gpc_domains[1].base, +}; + +static struct genpd_onecell_data imx_gpc_onecell_data = { + .domains = imx_gpc_onecell_domains, + .num_domains = 2, +}; + +static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap) +{ + struct imx_pm_domain *domain; + int i, ret; + + for (i = 0; i < 2; i++) { + domain = &imx_gpc_domains[i]; + domain->regmap = regmap; + domain->ipg_rate_mhz = 66; + + if (i == 1) { + domain->supply = devm_regulator_get(dev, "pu"); + if (IS_ERR(domain->supply)) + return PTR_ERR(domain->supply);; + + ret = imx_pgc_get_clocks(dev, domain); + if (ret) + goto clk_err; + + domain->base.power_on(&domain->base); + } + } + + for (i = 0; i < 2; i++) + pm_genpd_init(&imx_gpc_domains[i].base, NULL, false); + + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { + ret = of_genpd_add_provider_onecell(dev->of_node, + &imx_gpc_onecell_data); + if (ret) + goto genpd_err; + } + + return 0; + +genpd_err: + for (i = 0; i < 2; i++) + pm_genpd_remove(&imx_gpc_domains[i].base); + imx_pgc_put_clocks(&imx_gpc_domains[1]); +clk_err: + return ret; +} + +static int imx_gpc_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(imx_gpc_dt_ids, &pdev->dev); + const struct imx_gpc_dt_data *of_id_data = of_id->data; + struct device_node *pgc_node; + struct regmap *regmap; + struct resource *res; + void __iomem *base; + int ret; + + pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); + + /* bail out if DT too old and doesn't provide the necessary info */ + if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + !pgc_node) + return 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &imx_gpc_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&pdev->dev, "failed to init regmap: %d\n", + ret); + return ret; + } + + if (!pgc_node) { + /* old DT layout is only supported for mx6q aka 2 domains */ + if (of_id_data->num_domains != 2) { + dev_err(&pdev->dev, "could not find pgc DT node\n"); + return -ENODEV; + } + + ret = imx_gpc_old_dt_init(&pdev->dev, regmap); + if (ret) + return ret; + } else { + struct imx_pm_domain *domain; + struct platform_device *pd_pdev; + struct device_node *np; + struct clk *ipg_clk; + unsigned int ipg_rate_mhz; + int domain_index; + + ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(ipg_clk)) + return PTR_ERR(ipg_clk); + ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000; + + for_each_child_of_node(pgc_node, np) { + ret = of_property_read_u32(np, "reg", &domain_index); + if (ret) { + of_node_put(np); + return ret; + } + if (domain_index >= ARRAY_SIZE(imx_gpc_domains)) + continue; + + domain = &imx_gpc_domains[domain_index]; + domain->regmap = regmap; + domain->ipg_rate_mhz = ipg_rate_mhz; + + pd_pdev = platform_device_alloc("imx-pgc-power-domain", + domain_index); + if (!pd_pdev) { + of_node_put(np); + return -ENOMEM; + } + pd_pdev->dev.platform_data = domain; + pd_pdev->dev.parent = &pdev->dev; + pd_pdev->dev.of_node = np; + + ret = platform_device_add(pd_pdev); + if (ret) { + platform_device_put(pd_pdev); + of_node_put(np); + return ret; + } + } + } + + return 0; +} + +static int imx_gpc_remove(struct platform_device *pdev) +{ + int ret; + + /* + * If the old DT binding is used the toplevel driver needs to + * de-register the power domains + */ + if (!of_get_child_by_name(pdev->dev.of_node, "pgc")) { + of_genpd_del_provider(pdev->dev.of_node); + + ret = pm_genpd_remove(&imx_gpc_domains[1].base); + if (ret) + return ret; + imx_pgc_put_clocks(&imx_gpc_domains[1]); + + ret = pm_genpd_remove(&imx_gpc_domains[0].base); + if (ret) + return ret; + } + + return 0; +} + +static struct platform_driver imx_gpc_driver = { + .driver = { + .name = "imx-gpc", + .of_match_table = imx_gpc_dt_ids, + }, + .probe = imx_gpc_probe, + .remove = imx_gpc_remove, +}; +builtin_platform_driver(imx_gpc_driver) -- cgit v1.2.3 From e3a059c0ba9f2b8284065de6997770d4604077fa Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 6 Feb 2017 16:23:02 +0000 Subject: soc: zte: pm_domains: Remove redundant dev_err call in zx2967_pd_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Shawn Guo --- drivers/soc/zte/zx2967_pm_domains.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/zte/zx2967_pm_domains.c b/drivers/soc/zte/zx2967_pm_domains.c index 61c8d84bf315..c42aeaaa34ba 100644 --- a/drivers/soc/zte/zx2967_pm_domains.c +++ b/drivers/soc/zte/zx2967_pm_domains.c @@ -125,10 +125,8 @@ int zx2967_pd_probe(struct platform_device *pdev, res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pcubase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pcubase)) { - dev_err(&pdev->dev, "ioremap fail.\n"); + if (IS_ERR(pcubase)) return PTR_ERR(pcubase); - } for (i = 0; i < domain_num; ++i) { zx_pm_domains[i]->power_on = zx2967_power_on; -- cgit v1.2.3 From 71ce8430348f989d0c78871828d96e0e616056dd Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 6 Feb 2017 16:23:18 +0000 Subject: soc: zte: pm_domains: Remove .owner field for driver Remove .owner field if calls are used which set it automatically. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Wei Yongjun Signed-off-by: Shawn Guo --- drivers/soc/zte/zx296718_pm_domains.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/zte/zx296718_pm_domains.c b/drivers/soc/zte/zx296718_pm_domains.c index 5ed924fee855..4dc5d62ee81b 100644 --- a/drivers/soc/zte/zx296718_pm_domains.c +++ b/drivers/soc/zte/zx296718_pm_domains.c @@ -169,7 +169,6 @@ static const struct of_device_id zx296718_pm_domain_matches[] = { static struct platform_driver zx296718_pd_driver = { .driver = { .name = "zx296718-powerdomain", - .owner = THIS_MODULE, .of_match_table = zx296718_pm_domain_matches, }, .probe = zx296718_pd_probe, -- cgit v1.2.3 From 8848e1b14231a40ed66229fb3ee98519b32f2ae7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 10 Mar 2017 14:48:29 +0100 Subject: soc: renesas: Identify RZ/G1H Add support for identifying the RZ/G1H (r8a7742) SoC. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 330960312296..f0172e59c040 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -80,6 +80,11 @@ static const struct renesas_soc soc_rmobile_a1 __initconst __maybe_unused = { .id = 0x40, }; +static const struct renesas_soc soc_rz_g1h __initconst __maybe_unused = { + .family = &fam_rzg, + .id = 0x45, +}; + static const struct renesas_soc soc_rz_g1m __initconst __maybe_unused = { .family = &fam_rzg, .id = 0x47, @@ -150,6 +155,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A7740 { .compatible = "renesas,r8a7740", .data = &soc_rmobile_a1 }, #endif +#ifdef CONFIG_ARCH_R8A7742 + { .compatible = "renesas,r8a7742", .data = &soc_rz_g1h }, +#endif #ifdef CONFIG_ARCH_R8A7743 { .compatible = "renesas,r8a7743", .data = &soc_rz_g1m }, #endif -- cgit v1.2.3 From cd59de80dd34dd2d1a3ca97d7a6e712c048b135a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 10 Mar 2017 14:48:30 +0100 Subject: soc: renesas: Identify RZ/G1N Add support for identifying the RZ/G1N (r8a7744) SoC. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index f0172e59c040..b894cf64225b 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -90,6 +90,11 @@ static const struct renesas_soc soc_rz_g1m __initconst __maybe_unused = { .id = 0x47, }; +static const struct renesas_soc soc_rz_g1n __initconst __maybe_unused = { + .family = &fam_rzg, + .id = 0x4b, +}; + static const struct renesas_soc soc_rz_g1e __initconst __maybe_unused = { .family = &fam_rzg, .id = 0x4c, @@ -161,6 +166,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A7743 { .compatible = "renesas,r8a7743", .data = &soc_rz_g1m }, #endif +#ifdef CONFIG_ARCH_R8A7744 + { .compatible = "renesas,r8a7744", .data = &soc_rz_g1n }, +#endif #ifdef CONFIG_ARCH_R8A7745 { .compatible = "renesas,r8a7745", .data = &soc_rz_g1e }, #endif -- cgit v1.2.3 From a0ebf66267ebab093cd7e230e70d29249768d249 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 14 Mar 2017 19:10:27 +0200 Subject: soc: samsung: Do not build ARMv7 PMU drivers on ARMv8 The Exynos Power Management Unit (PMU) drivers contain quite large static arrays of register values necessary for given Exynos SoC to enter low power mode. All this data is useless for ARMv8 SoC like Exynos5433, because the image will not be shared between ARMv7 and ARMv8. Add additional Kconfig symbol for selecting the SoC-specific driver addons thus skipping the useless data in the final image (this is similar approach to chosen for Exynos clock controller drivers): - exynos-pmu driver will be compiled on both architectures ARMv7 and ARMv8, - additional driver_data for ARMv7 SoCs will not be built on ARMv8 and a macro will return NULL for them in of_device_id - this should be safe as these compatibles cannot match on ARMv7 and driver anyway handles NULL driver_data, - on ARMv8 compile only exynos-pmu driver which exposes the syscon-regmap for PMU address space. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Alim Akhtar --- drivers/soc/samsung/Kconfig | 8 +++++++- drivers/soc/samsung/Makefile | 4 +++- drivers/soc/samsung/exynos-pmu.c | 22 ++++++++++++++++------ drivers/soc/samsung/exynos-pmu.h | 3 +++ 4 files changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 245533907d1b..8b25bd55e648 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -8,7 +8,13 @@ if SOC_SAMSUNG config EXYNOS_PMU bool "Exynos PMU controller driver" if COMPILE_TEST - depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST) + depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) + select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS + +# There is no need to enable these drivers for ARMv8 +config EXYNOS_PMU_ARM_DRIVERS + bool "Exynos PMU ARMv7-specific driver extensions" if COMPILE_TEST + depends on EXYNOS_PMU config EXYNOS_PM_DOMAINS bool "Exynos PM domains" if COMPILE_TEST diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 3619f2ecddaa..4d7694a4e7a4 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -1,3 +1,5 @@ -obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \ +obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o + +obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ exynos5250-pmu.o exynos5420-pmu.o obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index 56d9244ff981..bd4a76f27bc2 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -68,28 +68,38 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode) } } +/* + * Split the data between ARM architectures because it is relatively big + * and useless on other arch. + */ +#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS +#define exynos_pmu_data_arm_ptr(data) (&data) +#else +#define exynos_pmu_data_arm_ptr(data) NULL +#endif + /* * PMU platform driver and devicetree bindings. */ static const struct of_device_id exynos_pmu_of_device_ids[] = { { .compatible = "samsung,exynos3250-pmu", - .data = &exynos3250_pmu_data, + .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data), }, { .compatible = "samsung,exynos4210-pmu", - .data = &exynos4210_pmu_data, + .data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data), }, { .compatible = "samsung,exynos4212-pmu", - .data = &exynos4212_pmu_data, + .data = exynos_pmu_data_arm_ptr(exynos4212_pmu_data), }, { .compatible = "samsung,exynos4412-pmu", - .data = &exynos4412_pmu_data, + .data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data), }, { .compatible = "samsung,exynos5250-pmu", - .data = &exynos5250_pmu_data, + .data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data), }, { .compatible = "samsung,exynos5420-pmu", - .data = &exynos5420_pmu_data, + .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data), }, { .compatible = "samsung,exynos5433-pmu", }, diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h index a469e366fead..40d4229abfb5 100644 --- a/drivers/soc/samsung/exynos-pmu.h +++ b/drivers/soc/samsung/exynos-pmu.h @@ -31,6 +31,8 @@ struct exynos_pmu_data { }; extern void __iomem *pmu_base_addr; + +#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS /* list of all exported SoC specific data */ extern const struct exynos_pmu_data exynos3250_pmu_data; extern const struct exynos_pmu_data exynos4210_pmu_data; @@ -38,6 +40,7 @@ extern const struct exynos_pmu_data exynos4212_pmu_data; extern const struct exynos_pmu_data exynos4412_pmu_data; extern const struct exynos_pmu_data exynos5250_pmu_data; extern const struct exynos_pmu_data exynos5420_pmu_data; +#endif extern void pmu_raw_writel(u32 val, u32 offset); extern u32 pmu_raw_readl(u32 offset); -- cgit v1.2.3 From 55b0baa2542f1dbaf33989eab5a26a23a8aca345 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:18 +0800 Subject: soc: imx: gpc: fix gpc clk get error handling We got a following kernel crash once supplying one more IPG clock in GPC node in devicetree. The original error handling of clocks get is a bit wrong that when reaching the maximum clock get error, the index 'i' is already GPC_CLK_MAX which can't be used as the array index for clk_put operations. [ 3.000110] imx-gpc 20dc000.gpc: more than 6 clocks [ 3.005141] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 3.013487] pgd = c0004000 [ 3.016300] [00000000] *pgd=00000000 [ 3.020060] Internal error: Oops: 805 [#1] SMP ARM [ 3.024957] Modules linked in: [ 3.028122] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 4.11.0-rc1-00056-g813791b-dirty #1140 [ 3.037801] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) [ 3.044435] task: ef298000 task.stack: ef294000 [ 3.049080] PC is at __clk_put+0x38/0xec [ 3.053103] LR is at 0x7f54ce9a [ 3.056345] pc : [] lr : [<7f54ce9a>] psr: 60000013 [ 3.056345] sp : ef295d48 ip : c8a582b2 fp : ef295d64 [ 3.068026] r10: ee9fc400 r9 : 00000000 r8 : ef398c10 [ 3.073354] r7 : ef398c10 r6 : c1071264 r5 : c10710f0 r4 : eea5be80 [ 3.079986] r3 : 00000000 r2 : 00000000 r1 : 00000100 r0 : 00000001 [ 3.086621] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none [ 3.093863] Control: 10c5387d Table: 1000404a DAC: 00000051 [ 3.099712] Process swapper/0 (pid: 1, stack limit = 0xef294210) [ 3.105823] Stack: (0xef295d48 to 0xef296000) ... [ 3.292660] Backtrace: [ 3.295222] [] (__clk_put) from [] (clk_put+0x18/0x1c) [ 3.302206] r6:c1071264 r5:c10710f0 r4:c107124c r3:00000001 [ 3.307977] [] (clk_put) from [] (imx_pgc_get_clocks+0x64/0x78) [ 3.315747] [] (imx_pgc_get_clocks) from [] (imx_gpc_probe+0x204/0x31c) [ 3.324209] r7:00000000 r6:c1070eb0 r5:00000001 r4:ef398c00 [ 3.329980] [] (imx_gpc_probe) from [] (platform_drv_probe+0x5c/0xc0) [ 3.338270] r10:c0f00608 r9:00000000 r8:00000000 r7:fffffdfb r6:c1070f20 r5:ef398c10 [ 3.346207] r4:ef398c10 [ 3.348849] [] (platform_drv_probe) from [] (driver_probe_device+0x214/0x2ec) [ 3.357835] r7:c1070f20 r6:00000000 r5:c18cea74 r4:ef398c10 [ 3.363607] [] (driver_probe_device) from [] (__driver_attach+0xc4/0xc8) [ 3.372159] r9:c0f8b858 r8:c0f8b850 r7:00000000 r6:ef398c44 r5:c1070f20 r4:ef398c10 [ 3.380017] [] (__driver_attach) from [] (bus_for_each_dev+0x7c/0xb0) [ 3.388304] r6:c05e4328 r5:c1070f20 r4:00000000 r3:00000000 [ 3.394074] [] (bus_for_each_dev) from [] (driver_attach+0x28/0x30) [ 3.402188] r6:c107f3e8 r5:eea5be00 r4:c1070f20 [ 3.406913] [] (driver_attach) from [] (bus_add_driver+0x19c/0x224) [ 3.415034] [] (bus_add_driver) from [] (driver_register+0x88/0x108) [ 3.423235] r7:c10e1000 r6:00000000 r5:c0f57d2c r4:c1070f20 [ 3.429004] [] (driver_register) from [] (__platform_driver_register+0x40/0x54) [ 3.438160] r5:c0f57d2c r4:00000006 [ 3.441846] [] (__platform_driver_register) from [] (imx_gpc_driver_init+0x18/0x20) [ 3.451360] [] (imx_gpc_driver_init) from [] (do_one_initcall+0x4c/0x180) [ 3.460008] [] (do_one_initcall) from [] (kernel_init_freeable+0x130/0x1f8) [ 3.468820] r9:c0f8b858 r8:c0f8b850 r6:c0fc2414 r5:c10e1000 r4:00000006 [ 3.475637] [] (kernel_init_freeable) from [] (kernel_init+0x18/0x124) [ 3.484014] r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0ae6ad4 [ 3.491951] r4:00000000 [ 3.494590] [] (kernel_init) from [] (ret_from_fork+0x14/0x24) [ 3.502267] r4:00000000 r3:ef294000 [ 3.505947] Code: e5943014 e5942018 e3530000 e3a01c01 (e5823000) [ 3.512215] ---[ end trace 375f9f2a5ddeff3c ]--- [ 3.517036] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b Cc: Lucas Stach Fixes: 721cabf6c660 ("soc: imx: move PGC handling to a new GPC driver") Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 1e9b3b81f466..c9bfdfd783d0 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -143,7 +143,7 @@ static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain) return 0; clk_err: - for (; i >= 0; i--) + while (i--) clk_put(domain->clk[i]); return ret; -- cgit v1.2.3 From 3a317f523570adfc9c5bf6d65dc4f831dada97b9 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:19 +0800 Subject: soc: imx: gpc: fix the wrong using of regmap cache Without providing the proper reg_defaults, the regmap registers first read out may be always 0 if enabling cache, which results in the following issue we met. e.g. During driver probe in imx6_pm_domain_power_on(): regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val); The PGC_PUPSCR register val is always 0 but it's actually 0xf01 in HW. Since GPC registers are tightly related to CPU bring up and may be changed in bootloader, we don't want to provide defaults. And the cache really does not save too much for GPC module. Therefore, simply disable cache to fix the issue and make life easy. Reviewed-by: Lucas Stach Fixes: 721cabf6c660 ("soc: imx: move PGC handling to a new GPC driver") Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index c9bfdfd783d0..7e6a672bf5f4 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -289,22 +289,12 @@ static bool imx_gpc_readable_reg(struct device *dev, unsigned int reg) return (reg % 4 == 0) && (reg <= 0x2ac); } -static bool imx_gpc_volatile_reg(struct device *dev, unsigned int reg) -{ - if (reg == GPC_CNTR) - return true; - - return false; -} - static const struct regmap_config imx_gpc_regmap_config = { - .cache_type = REGCACHE_FLAT, .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .readable_reg = imx_gpc_readable_reg, - .volatile_reg = imx_gpc_volatile_reg, .max_register = 0x2ac, }; -- cgit v1.2.3 From 15c3de4e188b567ab60dbff674002a7f4380af6a Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:20 +0800 Subject: soc: imx: gpc: fix domain_index sanity check issue ARRAY_SIZE(imx_gpc_domains) represents all power domains supported by different SoCs. Driver should use SoC specific of_id_data->num_domains instead to do power domain index sanity check. e.g. MX6Q supports two power domains while MX6SL supports three. Fixes: 721cabf6c660 ("soc: imx: move PGC handling to a new GPC driver") Acked-by: Lucas Stach Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 7e6a672bf5f4..ba6e7ab2c240 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -413,7 +413,7 @@ static int imx_gpc_probe(struct platform_device *pdev) of_node_put(np); return ret; } - if (domain_index >= ARRAY_SIZE(imx_gpc_domains)) + if (domain_index >= of_id_data->num_domains) continue; domain = &imx_gpc_domains[domain_index]; -- cgit v1.2.3 From 5a42d1198901a13ff46e1d13b91a338d74224dbe Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:21 +0800 Subject: soc: imx: gpc: fix imx6sl gpc power domain regression Commit 721cabf6c660 ("soc: imx: move PGC handling to a new GPC driver") broke the MX6SL GPC power domain support. It always got the following error: [ 1.248364] imx-gpc 20dc000.gpc: could not find pgc DT node This patch adds back the legecy support. Fixes: 721cabf6c660 ("soc: imx: move PGC handling to a new GPC driver") Signed-off-by: Dong Aisheng Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index ba6e7ab2c240..9a2354ea48c9 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -309,12 +309,13 @@ static struct genpd_onecell_data imx_gpc_onecell_data = { .num_domains = 2, }; -static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap) +static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap, + unsigned int num_domains) { struct imx_pm_domain *domain; int i, ret; - for (i = 0; i < 2; i++) { + for (i = 0; i < num_domains; i++) { domain = &imx_gpc_domains[i]; domain->regmap = regmap; domain->ipg_rate_mhz = 66; @@ -332,7 +333,7 @@ static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap) } } - for (i = 0; i < 2; i++) + for (i = 0; i < num_domains; i++) pm_genpd_init(&imx_gpc_domains[i].base, NULL, false); if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { @@ -345,7 +346,7 @@ static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap) return 0; genpd_err: - for (i = 0; i < 2; i++) + for (i = 0; i < num_domains; i++) pm_genpd_remove(&imx_gpc_domains[i].base); imx_pgc_put_clocks(&imx_gpc_domains[1]); clk_err: @@ -385,13 +386,8 @@ static int imx_gpc_probe(struct platform_device *pdev) } if (!pgc_node) { - /* old DT layout is only supported for mx6q aka 2 domains */ - if (of_id_data->num_domains != 2) { - dev_err(&pdev->dev, "could not find pgc DT node\n"); - return -ENODEV; - } - - ret = imx_gpc_old_dt_init(&pdev->dev, regmap); + ret = imx_gpc_old_dt_init(&pdev->dev, regmap, + of_id_data->num_domains); if (ret) return ret; } else { -- cgit v1.2.3 From 6e6e339cc185fdd27d476764637b5b3b6738cf04 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:22 +0800 Subject: soc: imx: gpc: fix comment when power up domain The correct comment should be power up domain. Reviewed-by: Lucas Stach Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 9a2354ea48c9..afb02f9cc231 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -104,7 +104,7 @@ static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_PDN_OFFS, 0x1, 0x1); - /* Read ISO and ISO2SW power down delays */ + /* Read ISO and ISO2SW power up delays */ regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val); sw = val & 0x3f; sw2iso = (val >> 8) & 0x3f; -- cgit v1.2.3 From fbb0b4402a7132cd11cc3e63b12f543654bd1785 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:23 +0800 Subject: soc: imx: gpc: keep PGC_X_CTRL name align with reference manual Instead of GPC_PGC_PDN_OFFS, naming it as GPC_PGC_CTRL_OFFS which is defined in reference manual for better reading. Reviewed-by: Lucas Stach Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index afb02f9cc231..3c612487e0fd 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -21,7 +21,7 @@ #define GPC_CNTR 0x000 -#define GPC_PGC_PDN_OFFS 0x0 +#define GPC_PGC_CTRL_OFFS 0x0 #define GPC_PGC_PUPSCR_OFFS 0x4 #define GPC_PGC_PDNSCR_OFFS 0x8 #define GPC_PGC_SW2ISO_SHIFT 0x8 @@ -65,7 +65,7 @@ static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd) iso2sw = (val >> 8) & 0x3f; /* Gate off domain when powered down */ - regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_PDN_OFFS, + regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, 0x1, 0x1); /* Request GPC to power down domain */ @@ -101,7 +101,7 @@ static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) clk_prepare_enable(pd->clk[i]); /* Gate off domain when powered down */ - regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_PDN_OFFS, + regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, 0x1, 0x1); /* Read ISO and ISO2SW power up delays */ -- cgit v1.2.3 From bd01f064af2a5de4293c5401e01de5a6f951f4a5 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 23 Mar 2017 12:53:25 +0800 Subject: soc: imx: gpc: remove unnecessary readable_reg callback It is not really necessary to provide the current .readable_reg implementation as we know what we're doing in our driver and the regmap core has already done the partial check for available maximum regs. Acked-by: Lucas Stach Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 3c612487e0fd..4294287e5f6c 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -284,18 +284,10 @@ static const struct of_device_id imx_gpc_dt_ids[] = { { } }; -static bool imx_gpc_readable_reg(struct device *dev, unsigned int reg) -{ - return (reg % 4 == 0) && (reg <= 0x2ac); -} - static const struct regmap_config imx_gpc_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - - .readable_reg = imx_gpc_readable_reg, - .max_register = 0x2ac, }; -- cgit v1.2.3 From 5e7d4c65294174d6f58fe36df3edd55cd3b859d6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 23 Feb 2017 18:11:57 +0100 Subject: soc/tegra: Implement Tegra186 PMC support The power management controller on Tegra186 has changed in backwards- incompatible ways with respect to earlier generations. This implements a new driver that supports inversion of the PMU interrupt as well as the "recovery", "bootloader" and "forced-recovery" reboot commands. Acked-by: Rob Herring Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 13 +++ drivers/soc/tegra/Makefile | 3 +- drivers/soc/tegra/pmc-tegra186.c | 169 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/tegra/pmc-tegra186.c (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index e5e124c07066..208d6edb3fdb 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -12,6 +12,7 @@ config ARCH_TEGRA_2x_SOC select PINCTRL_TEGRA20 select PL310_ERRATA_727915 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0 + select SOC_TEGRA_PMC select TEGRA_TIMER help Support for NVIDIA Tegra AP20 and T20 processors, based on the @@ -23,6 +24,7 @@ config ARCH_TEGRA_3x_SOC select ARM_ERRATA_764369 if SMP select PINCTRL_TEGRA30 select PL310_ERRATA_769419 if CACHE_L2X0 + select SOC_TEGRA_PMC select TEGRA_TIMER help Support for NVIDIA Tegra T30 processor family, based on the @@ -33,6 +35,7 @@ config ARCH_TEGRA_114_SOC select ARM_ERRATA_798181 if SMP select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA114 + select SOC_TEGRA_PMC select TEGRA_TIMER help Support for NVIDIA Tegra T114 processor family, based on the @@ -42,6 +45,7 @@ config ARCH_TEGRA_124_SOC bool "Enable support for Tegra124 family" select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA124 + select SOC_TEGRA_PMC select TEGRA_TIMER help Support for NVIDIA Tegra T124 processor family, based on the @@ -55,6 +59,7 @@ if ARM64 config ARCH_TEGRA_132_SOC bool "NVIDIA Tegra132 SoC" select PINCTRL_TEGRA124 + select SOC_TEGRA_PMC help Enable support for NVIDIA Tegra132 SoC, based on the Denver ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC, @@ -64,6 +69,7 @@ config ARCH_TEGRA_132_SOC config ARCH_TEGRA_210_SOC bool "NVIDIA Tegra210 SoC" select PINCTRL_TEGRA210 + select SOC_TEGRA_PMC help Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1, the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53 @@ -83,6 +89,7 @@ config ARCH_TEGRA_186_SOC select TEGRA_BPMP select TEGRA_HSP_MBOX select TEGRA_IVC + select SOC_TEGRA_PMC_TEGRA186 help Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a combination of Denver and Cortex-A57 CPU cores and a GPU based on @@ -93,3 +100,9 @@ config ARCH_TEGRA_186_SOC endif endif + +config SOC_TEGRA_PMC + bool + +config SOC_TEGRA_PMC_TEGRA186 + bool diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index ae857ff7d53d..b4425e4319ff 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -1,4 +1,5 @@ obj-y += fuse/ obj-y += common.o -obj-y += pmc.o +obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o +obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o diff --git a/drivers/soc/tegra/pmc-tegra186.c b/drivers/soc/tegra/pmc-tegra186.c new file mode 100644 index 000000000000..6f5c6f98ba92 --- /dev/null +++ b/drivers/soc/tegra/pmc-tegra186.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#define pr_fmt(fmt) "tegra-pmc: " fmt + +#include +#include +#include +#include +#include + +#include + +#define PMC_CNTRL 0x000 +#define PMC_CNTRL_MAIN_RST BIT(4) + +#define PMC_RST_STATUS 0x070 + +#define WAKE_AOWAKE_CTRL 0x4f4 +#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) + +#define SCRATCH_SCRATCH0 0x2000 +#define SCRATCH_SCRATCH0_MODE_RECOVERY BIT(31) +#define SCRATCH_SCRATCH0_MODE_BOOTLOADER BIT(30) +#define SCRATCH_SCRATCH0_MODE_RCM BIT(1) +#define SCRATCH_SCRATCH0_MODE_MASK (SCRATCH_SCRATCH0_MODE_RECOVERY | \ + SCRATCH_SCRATCH0_MODE_BOOTLOADER | \ + SCRATCH_SCRATCH0_MODE_RCM) + +struct tegra_pmc { + struct device *dev; + void __iomem *regs; + void __iomem *wake; + void __iomem *aotag; + void __iomem *scratch; + + void (*system_restart)(enum reboot_mode mode, const char *cmd); + struct notifier_block restart; +}; + +static int tegra186_pmc_restart_notify(struct notifier_block *nb, + unsigned long action, + void *data) +{ + struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, restart); + const char *cmd = data; + u32 value; + + value = readl(pmc->scratch + SCRATCH_SCRATCH0); + value &= ~SCRATCH_SCRATCH0_MODE_MASK; + + if (cmd) { + if (strcmp(cmd, "recovery") == 0) + value |= SCRATCH_SCRATCH0_MODE_RECOVERY; + + if (strcmp(cmd, "bootloader") == 0) + value |= SCRATCH_SCRATCH0_MODE_BOOTLOADER; + + if (strcmp(cmd, "forced-recovery") == 0) + value |= SCRATCH_SCRATCH0_MODE_RCM; + } + + writel(value, pmc->scratch + SCRATCH_SCRATCH0); + + /* + * If available, call the system restart implementation that was + * registered earlier (typically PSCI). + */ + if (pmc->system_restart) { + pmc->system_restart(reboot_mode, cmd); + return NOTIFY_DONE; + } + + /* reset everything but SCRATCH0_SCRATCH0 and PMC_RST_STATUS */ + value = readl(pmc->regs + PMC_CNTRL); + value |= PMC_CNTRL_MAIN_RST; + writel(value, pmc->regs + PMC_CNTRL); + + return NOTIFY_DONE; +} + +static int tegra186_pmc_setup(struct tegra_pmc *pmc) +{ + struct device_node *np = pmc->dev->of_node; + bool invert; + u32 value; + + invert = of_property_read_bool(np, "nvidia,invert-interrupt"); + + value = readl(pmc->wake + WAKE_AOWAKE_CTRL); + + if (invert) + value |= WAKE_AOWAKE_CTRL_INTR_POLARITY; + else + value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY; + + writel(value, pmc->wake + WAKE_AOWAKE_CTRL); + + /* + * We need to hook any system restart implementation registered + * previously so we can write SCRATCH_SCRATCH0 before reset. + */ + pmc->system_restart = arm_pm_restart; + arm_pm_restart = NULL; + + pmc->restart.notifier_call = tegra186_pmc_restart_notify; + pmc->restart.priority = 128; + + return register_restart_handler(&pmc->restart); +} + +static int tegra186_pmc_probe(struct platform_device *pdev) +{ + struct tegra_pmc *pmc; + struct resource *res; + + pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); + if (!pmc) + return -ENOMEM; + + pmc->dev = &pdev->dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmc"); + pmc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->regs)) + return PTR_ERR(pmc->regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake"); + pmc->wake = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->wake)) + return PTR_ERR(pmc->wake); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag"); + pmc->aotag = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->aotag)) + return PTR_ERR(pmc->aotag); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch"); + pmc->scratch = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->scratch)) + return PTR_ERR(pmc->scratch); + + return tegra186_pmc_setup(pmc); +} + +static const struct of_device_id tegra186_pmc_of_match[] = { + { .compatible = "nvidia,tegra186-pmc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tegra186_pmc_of_match); + +static struct platform_driver tegra186_pmc_driver = { + .driver = { + .name = "tegra186-pmc", + .of_match_table = tegra186_pmc_of_match, + }, + .probe = tegra186_pmc_probe, +}; +builtin_platform_driver(tegra186_pmc_driver); -- cgit v1.2.3 From 1859217bec83ebd1b4a220df396905a87285129b Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Nov 2016 14:03:01 -0500 Subject: soc: tegra: make fuse-tegra explicitly non-modular The Makefiles currently controlling compilation of this code is: drivers/soc/tegra/Makefile:obj-y += fuse/ drivers/soc/tegra/fuse/Makefile:obj-y += fuse-tegra.o ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modularity so that when reading the driver there is no doubt it is builtin-only. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. Cc: Stephen Warren Cc: Thierry Reding Cc: Alexandre Courbot Cc: linux-tegra@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index de2c1bfe28b5..7413f60fa855 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -168,7 +168,7 @@ static struct platform_driver tegra_fuse_driver = { }, .probe = tegra_fuse_probe, }; -module_platform_driver(tegra_fuse_driver); +builtin_platform_driver(tegra_fuse_driver); bool __init tegra_fuse_read_spare(unsigned int spare) { -- cgit v1.2.3 From 7e10cf743634a6b0f3cf63046c49294b38254fe9 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Mar 2017 13:42:54 +0100 Subject: soc/tegra: Move Tegra flowctrl driver The flowctrl driver is required for both ARM and ARM64 Tegra devices and in order to enable support for it for ARM64, move the Tegra flowctrl driver into drivers/soc/tegra. By moving the flowctrl driver, tegra_flowctrl_init() is now called by via an early initcall and to prevent this function from attempting to mapping IO space for a non-Tegra device, a test for 'soc_is_tegra()' is also added. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 7 ++ drivers/soc/tegra/Makefile | 1 + drivers/soc/tegra/flowctrl.c | 187 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 drivers/soc/tegra/flowctrl.c (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 208d6edb3fdb..c7e8ddfb574e 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -12,6 +12,7 @@ config ARCH_TEGRA_2x_SOC select PINCTRL_TEGRA20 select PL310_ERRATA_727915 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0 + select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC select TEGRA_TIMER help @@ -24,6 +25,7 @@ config ARCH_TEGRA_3x_SOC select ARM_ERRATA_764369 if SMP select PINCTRL_TEGRA30 select PL310_ERRATA_769419 if CACHE_L2X0 + select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC select TEGRA_TIMER help @@ -35,6 +37,7 @@ config ARCH_TEGRA_114_SOC select ARM_ERRATA_798181 if SMP select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA114 + select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC select TEGRA_TIMER help @@ -45,6 +48,7 @@ config ARCH_TEGRA_124_SOC bool "Enable support for Tegra124 family" select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA124 + select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC select TEGRA_TIMER help @@ -101,6 +105,9 @@ config ARCH_TEGRA_186_SOC endif endif +config SOC_TEGRA_FLOWCTRL + bool + config SOC_TEGRA_PMC bool diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index b4425e4319ff..4f81dd55e5d1 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -1,5 +1,6 @@ obj-y += fuse/ obj-y += common.o +obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o diff --git a/drivers/soc/tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c new file mode 100644 index 000000000000..3a5a1cb9ae90 --- /dev/null +++ b/drivers/soc/tegra/flowctrl.c @@ -0,0 +1,187 @@ +/* + * drivers/soc/tegra/flowctrl.c + * + * Functions and macros to control the flowcontroller + * + * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static u8 flowctrl_offset_halt_cpu[] = { + FLOW_CTRL_HALT_CPU0_EVENTS, + FLOW_CTRL_HALT_CPU1_EVENTS, + FLOW_CTRL_HALT_CPU1_EVENTS + 8, + FLOW_CTRL_HALT_CPU1_EVENTS + 16, +}; + +static u8 flowctrl_offset_cpu_csr[] = { + FLOW_CTRL_CPU0_CSR, + FLOW_CTRL_CPU1_CSR, + FLOW_CTRL_CPU1_CSR + 8, + FLOW_CTRL_CPU1_CSR + 16, +}; + +static void __iomem *tegra_flowctrl_base; + +static void flowctrl_update(u8 offset, u32 value) +{ + if (WARN_ONCE(!tegra_flowctrl_base, + "Tegra flowctrl not initialised!\n")) + return; + + writel(value, tegra_flowctrl_base + offset); + + /* ensure the update has reached the flow controller */ + wmb(); + readl_relaxed(tegra_flowctrl_base + offset); +} + +u32 flowctrl_read_cpu_csr(unsigned int cpuid) +{ + u8 offset = flowctrl_offset_cpu_csr[cpuid]; + + if (WARN_ONCE(!tegra_flowctrl_base, + "Tegra flowctrl not initialised!\n")) + return 0; + + return readl(tegra_flowctrl_base + offset); +} + +void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value) +{ + return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value); +} + +void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value) +{ + return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value); +} + +void flowctrl_cpu_suspend_enter(unsigned int cpuid) +{ + unsigned int reg; + int i; + + reg = flowctrl_read_cpu_csr(cpuid); + switch (tegra_get_chip_id()) { + case TEGRA20: + /* clear wfe bitmap */ + reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; + /* clear wfi bitmap */ + reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; + /* pwr gating on wfe */ + reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; + break; + case TEGRA30: + case TEGRA114: + case TEGRA124: + /* clear wfe bitmap */ + reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; + /* clear wfi bitmap */ + reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; + /* pwr gating on wfi */ + reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; + break; + } + reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */ + reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */ + reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */ + flowctrl_write_cpu_csr(cpuid, reg); + + for (i = 0; i < num_possible_cpus(); i++) { + if (i == cpuid) + continue; + reg = flowctrl_read_cpu_csr(i); + reg |= FLOW_CTRL_CSR_EVENT_FLAG; + reg |= FLOW_CTRL_CSR_INTR_FLAG; + flowctrl_write_cpu_csr(i, reg); + } +} + +void flowctrl_cpu_suspend_exit(unsigned int cpuid) +{ + unsigned int reg; + + /* Disable powergating via flow controller for CPU0 */ + reg = flowctrl_read_cpu_csr(cpuid); + switch (tegra_get_chip_id()) { + case TEGRA20: + /* clear wfe bitmap */ + reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; + /* clear wfi bitmap */ + reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; + break; + case TEGRA30: + case TEGRA114: + case TEGRA124: + /* clear wfe bitmap */ + reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; + /* clear wfi bitmap */ + reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; + break; + } + reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */ + reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */ + reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */ + flowctrl_write_cpu_csr(cpuid, reg); +} + +static const struct of_device_id matches[] __initconst = { + { .compatible = "nvidia,tegra124-flowctrl" }, + { .compatible = "nvidia,tegra114-flowctrl" }, + { .compatible = "nvidia,tegra30-flowctrl" }, + { .compatible = "nvidia,tegra20-flowctrl" }, + { } +}; + +static int __init tegra_flowctrl_init(void) +{ + /* hardcoded fallback if device tree node is missing */ + unsigned long base = 0x60007000; + unsigned long size = SZ_4K; + struct device_node *np; + + if (!soc_is_tegra()) + return 0; + + np = of_find_matching_node(NULL, matches); + if (np) { + struct resource res; + + if (of_address_to_resource(np, 0, &res) == 0) { + size = resource_size(&res); + base = res.start; + } + + of_node_put(np); + } + + tegra_flowctrl_base = ioremap_nocache(base, size); + if (!tegra_flowctrl_base) + return -ENXIO; + + return 0; +} +early_initcall(tegra_flowctrl_init); -- cgit v1.2.3 From 841fd94c43a4034f08eb830ef7b93a441b4d7378 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Mar 2017 13:42:55 +0100 Subject: soc/tegra: flowctrl: Add basic platform driver Add a simple platform driver for the flowctrl module so that it gets registered as a proper device. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/flowctrl.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c index 3a5a1cb9ae90..25eddfc8475d 100644 --- a/drivers/soc/tegra/flowctrl.c +++ b/drivers/soc/tegra/flowctrl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -47,7 +48,7 @@ static void __iomem *tegra_flowctrl_base; static void flowctrl_update(u8 offset, u32 value) { - if (WARN_ONCE(!tegra_flowctrl_base, + if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base), "Tegra flowctrl not initialised!\n")) return; @@ -62,7 +63,7 @@ u32 flowctrl_read_cpu_csr(unsigned int cpuid) { u8 offset = flowctrl_offset_cpu_csr[cpuid]; - if (WARN_ONCE(!tegra_flowctrl_base, + if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base), "Tegra flowctrl not initialised!\n")) return 0; @@ -148,7 +149,22 @@ void flowctrl_cpu_suspend_exit(unsigned int cpuid) flowctrl_write_cpu_csr(cpuid, reg); } -static const struct of_device_id matches[] __initconst = { +static int tegra_flowctrl_probe(struct platform_device *pdev) +{ + void __iomem *base = tegra_flowctrl_base; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra_flowctrl_base)) + return PTR_ERR(base); + + iounmap(base); + + return 0; +} + +static const struct of_device_id tegra_flowctrl_match[] = { { .compatible = "nvidia,tegra124-flowctrl" }, { .compatible = "nvidia,tegra114-flowctrl" }, { .compatible = "nvidia,tegra30-flowctrl" }, @@ -156,6 +172,16 @@ static const struct of_device_id matches[] __initconst = { { } }; +static struct platform_driver tegra_flowctrl_driver = { + .driver = { + .name = "tegra-flowctrl", + .suppress_bind_attrs = true, + .of_match_table = tegra_flowctrl_match, + }, + .probe = tegra_flowctrl_probe, +}; +builtin_platform_driver(tegra_flowctrl_driver); + static int __init tegra_flowctrl_init(void) { /* hardcoded fallback if device tree node is missing */ @@ -166,7 +192,7 @@ static int __init tegra_flowctrl_init(void) if (!soc_is_tegra()) return 0; - np = of_find_matching_node(NULL, matches); + np = of_find_matching_node(NULL, tegra_flowctrl_match); if (np) { struct resource res; -- cgit v1.2.3 From 1fd09e5d884a5ff4060948e0cf8f5d7eed16e936 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Mar 2017 13:42:58 +0100 Subject: soc/tegra: Add initial flowctrl support for Tegra132/210 Tegra132 and Tegra210 support the flowctrl module and so add initial support for these devices. Please note that Tegra186 does not support the flowctrl module, so update the initialisation function such that we do not fall back and attempt to map the 'hardcoded' address range for Tegra186. Furthermore 64-bit Tegra devices have always had the flowctrl node defined in their device-tree and so only use the 'hardcoded' addresses for 32-bit Tegra devices. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 2 ++ drivers/soc/tegra/flowctrl.c | 31 +++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index c7e8ddfb574e..dcf088db40b6 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -63,6 +63,7 @@ if ARM64 config ARCH_TEGRA_132_SOC bool "NVIDIA Tegra132 SoC" select PINCTRL_TEGRA124 + select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC help Enable support for NVIDIA Tegra132 SoC, based on the Denver @@ -73,6 +74,7 @@ config ARCH_TEGRA_132_SOC config ARCH_TEGRA_210_SOC bool "NVIDIA Tegra210 SoC" select PINCTRL_TEGRA210 + select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC help Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1, diff --git a/drivers/soc/tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c index 25eddfc8475d..0e345c05fc65 100644 --- a/drivers/soc/tegra/flowctrl.c +++ b/drivers/soc/tegra/flowctrl.c @@ -165,6 +165,7 @@ static int tegra_flowctrl_probe(struct platform_device *pdev) } static const struct of_device_id tegra_flowctrl_match[] = { + { .compatible = "nvidia,tegra210-flowctrl" }, { .compatible = "nvidia,tegra124-flowctrl" }, { .compatible = "nvidia,tegra114-flowctrl" }, { .compatible = "nvidia,tegra30-flowctrl" }, @@ -184,9 +185,7 @@ builtin_platform_driver(tegra_flowctrl_driver); static int __init tegra_flowctrl_init(void) { - /* hardcoded fallback if device tree node is missing */ - unsigned long base = 0x60007000; - unsigned long size = SZ_4K; + struct resource res; struct device_node *np; if (!soc_is_tegra()) @@ -194,17 +193,29 @@ static int __init tegra_flowctrl_init(void) np = of_find_matching_node(NULL, tegra_flowctrl_match); if (np) { - struct resource res; - - if (of_address_to_resource(np, 0, &res) == 0) { - size = resource_size(&res); - base = res.start; + if (of_address_to_resource(np, 0, &res) < 0) { + pr_err("failed to get flowctrl register\n"); + return -ENXIO; } - of_node_put(np); + } else if (IS_ENABLED(CONFIG_ARM)) { + /* + * Hardcoded fallback for 32-bit Tegra + * devices if device tree node is missing. + */ + res.start = 0x60007000; + res.end = 0x60007fff; + res.flags = IORESOURCE_MEM; + } else { + /* + * At this point we're running on a Tegra, + * that doesn't support the flow controller + * (eg. Tegra186), so just return. + */ + return 0; } - tegra_flowctrl_base = ioremap_nocache(base, size); + tegra_flowctrl_base = ioremap_nocache(res.start, resource_size(&res)); if (!tegra_flowctrl_base) return -ENXIO; -- cgit v1.2.3 From 52835d59fc6cc7f3c3cfdb4a194ddc9ebd6c0c31 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Tue, 4 Apr 2017 08:59:27 -0700 Subject: soc: ti: Add ti_sci_pm_domains driver Introduce a ti_sci_pm_domains driver to act as a generic pm domain provider to allow each device to attach and associate it's ti-sci-id so that it can be controlled through the TI SCI protocol. This driver implements a simple genpd where each device node has a phandle to the power domain node and also must provide an index which represents the ID to be passed with TI SCI representing the device using a single phandle cell. The driver manually parses the phandle to get the cell value. Through this interface the genpd dev_ops start and stop hooks will use TI SCI to turn on and off each device as determined by pm_runtime usage. Reviewed-by: Kevin Hilman Acked-by: Santosh Shilimkar Reviewed-by: Ulf Hansson Signed-off-by: Keerthy Signed-off-by: Nishanth Menon Signed-off-by: Dave Gerlach Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/Kconfig | 12 +++ drivers/soc/ti/Makefile | 1 + drivers/soc/ti/ti_sci_pm_domains.c | 202 +++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/soc/ti/ti_sci_pm_domains.c (limited to 'drivers/soc') diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index 3557c5e32a93..39e152abe6b9 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -38,4 +38,16 @@ config WKUP_M3_IPC to communicate and use the Wakeup M3 for PM features like suspend resume and boots it using wkup_m3_rproc driver. +config TI_SCI_PM_DOMAINS + tristate "TI SCI PM Domains Driver" + depends on TI_SCI_PROTOCOL + depends on PM_GENERIC_DOMAINS + help + Generic power domain implementation for TI device implementing + the TI SCI protocol. + + To compile this as a module, choose M here. The module will be + called ti_sci_pm_domains. Note this is needed early in boot before + rootfs may be available. + endif # SOC_TI diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index 48ff3a79634f..7d572736c86e 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o +obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c new file mode 100644 index 000000000000..d9dccb0c3a2a --- /dev/null +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -0,0 +1,202 @@ +/* + * TI SCI Generic Power Domain Driver + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://urldefense.proofpoint.com/v2/url?u=http-3A__www.ti.com_&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PQcxBKCX5YTpkKY057SbK10&r=XBn1JQGPwR8CsE7xpP3wPlG6DQU7qw8ym65xieNZ4hY&m=R6qGiR9DbG1C3EF_0mL-m-qkmSO64GklbFWpUzqt8fY&s=YTWcQCWi5lnIf4XHDLq1XDd4JbZv9xpqOwdPD8xEdZE&e= + * J Keerthy + * Dave Gerlach + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ti_sci_genpd_dev_data: holds data needed for every device attached + * to this genpd + * @idx: index of the device that identifies it with the system + * control processor. + */ +struct ti_sci_genpd_dev_data { + int idx; +}; + +/** + * struct ti_sci_pm_domain: TI specific data needed for power domain + * @ti_sci: handle to TI SCI protocol driver that provides ops to + * communicate with system control processor. + * @dev: pointer to dev for the driver for devm allocs + * @pd: generic_pm_domain for use with the genpd framework + */ +struct ti_sci_pm_domain { + const struct ti_sci_handle *ti_sci; + struct device *dev; + struct generic_pm_domain pd; +}; + +#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) + +/** + * ti_sci_dev_id(): get prepopulated ti_sci id from struct dev + * @dev: pointer to device associated with this genpd + * + * Returns device_id stored from ti,sci_id property + */ +static int ti_sci_dev_id(struct device *dev) +{ + struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev); + struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data; + + return sci_dev_data->idx; +} + +/** + * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle + * @dev: pointer to device associated with this genpd + * + * Returns ti_sci_handle to be used to communicate with system + * control processor. + */ +static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev) +{ + struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain); + struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd); + + return ti_sci_genpd->ti_sci; +} + +/** + * ti_sci_dev_start(): genpd device start hook called to turn device on + * @dev: pointer to device associated with this genpd to be powered on + */ +static int ti_sci_dev_start(struct device *dev) +{ + const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev); + int idx = ti_sci_dev_id(dev); + + return ti_sci->ops.dev_ops.get_device(ti_sci, idx); +} + +/** + * ti_sci_dev_stop(): genpd device stop hook called to turn device off + * @dev: pointer to device associated with this genpd to be powered off + */ +static int ti_sci_dev_stop(struct device *dev) +{ + const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev); + int idx = ti_sci_dev_id(dev); + + return ti_sci->ops.dev_ops.put_device(ti_sci, idx); +} + +static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct device_node *np = dev->of_node; + struct of_phandle_args pd_args; + struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain); + const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci; + struct ti_sci_genpd_dev_data *sci_dev_data; + struct generic_pm_domain_data *genpd_data; + int idx, ret = 0; + + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &pd_args); + if (ret < 0) + return ret; + + if (pd_args.args_count != 1) + return -EINVAL; + + idx = pd_args.args[0]; + + /* + * Check the validity of the requested idx, if the index is not valid + * the PMMC will return a NAK here and we will not allocate it. + */ + ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx); + if (ret) + return -EINVAL; + + sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL); + if (!sci_dev_data) + return -ENOMEM; + + sci_dev_data->idx = idx; + + genpd_data = dev_gpd_data(dev); + genpd_data->data = sci_dev_data; + + return 0; +} + +static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev); + struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data; + + kfree(sci_dev_data); + genpd_data->data = NULL; +} + +static const struct of_device_id ti_sci_pm_domain_matches[] = { + { .compatible = "ti,sci-pm-domain", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); + +static int ti_sci_pm_domain_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct ti_sci_pm_domain *ti_sci_pd; + int ret; + + ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL); + if (!ti_sci_pd) + return -ENOMEM; + + ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev); + if (IS_ERR(ti_sci_pd->ti_sci)) + return PTR_ERR(ti_sci_pd->ti_sci); + + ti_sci_pd->dev = dev; + + ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev; + ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev; + + ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start; + ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop; + + pm_genpd_init(&ti_sci_pd->pd, NULL, true); + + ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd); + + return ret; +} + +static struct platform_driver ti_sci_pm_domains_driver = { + .probe = ti_sci_pm_domain_probe, + .driver = { + .name = "ti_sci_pm_domains", + .of_match_table = ti_sci_pm_domain_matches, + }, +}; +module_platform_driver(ti_sci_pm_domains_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver"); +MODULE_AUTHOR("Dave Gerlach"); -- cgit v1.2.3 From 03aa12629fc4f73acf28e519c9ee9cb1f5dd3706 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 28 Mar 2017 08:19:45 -0700 Subject: soc: imx: Add GPCv2 power gating driver Add code allowing for control of various power domains managed by GPCv2 IP block found in i.MX7 series of SoCs. Power domains covered by this patch are: - PCIE PHY - MIPI PHY - USB HSIC PHY - USB OTG1/2 PHY Support for any other power domain controlled by GPC is not present, and can be added at some later point. Testing of this code was done against a PCIe driver. Cc: yurovsky@gmail.com Cc: Lucas Stach Cc: Fabio Estevam Cc: Dong Aisheng Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Andrey Smirnov Signed-off-by: Shawn Guo --- drivers/soc/Kconfig | 1 + drivers/soc/imx/Kconfig | 9 ++ drivers/soc/imx/Makefile | 1 + drivers/soc/imx/gpcv2.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 374 insertions(+) create mode 100644 drivers/soc/imx/Kconfig create mode 100644 drivers/soc/imx/gpcv2.c (limited to 'drivers/soc') diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index f09023f7ab11..89435435f213 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -2,6 +2,7 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" +source "drivers/soc/imx/Kconfig" source "drivers/soc/mediatek/Kconfig" source "drivers/soc/qcom/Kconfig" source "drivers/soc/rockchip/Kconfig" diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig new file mode 100644 index 000000000000..357a5d8f8da0 --- /dev/null +++ b/drivers/soc/imx/Kconfig @@ -0,0 +1,9 @@ +menu "i.MX SoC drivers" + +config IMX7_PM_DOMAINS + bool "i.MX7 PM domains" + select PM_GENERIC_DOMAINS + depends on SOC_IMX7D || (COMPILE_TEST && OF) + default y if SOC_IMX7D + +endmenu diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 35861f5b2802..5b6e396c1121 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -1 +1,2 @@ obj-y += gpc.o +obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c new file mode 100644 index 000000000000..3039072911a5 --- /dev/null +++ b/drivers/soc/imx/gpcv2.c @@ -0,0 +1,363 @@ +/* + * Copyright 2017 Impinj, Inc + * Author: Andrey Smirnov + * + * Based on the code of analogus driver: + * + * Copyright 2015-2017 Pengutronix, Lucas Stach + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include + +#define GPC_LPCR_A7_BSC 0x000 + +#define GPC_PGC_CPU_MAPPING 0x0ec +#define USB_HSIC_PHY_A7_DOMAIN BIT(6) +#define USB_OTG2_PHY_A7_DOMAIN BIT(5) +#define USB_OTG1_PHY_A7_DOMAIN BIT(4) +#define PCIE_PHY_A7_DOMAIN BIT(3) +#define MIPI_PHY_A7_DOMAIN BIT(2) + +#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 +#define GPC_PU_PGC_SW_PDN_REQ 0x104 +#define USB_HSIC_PHY_SW_Pxx_REQ BIT(4) +#define USB_OTG2_PHY_SW_Pxx_REQ BIT(3) +#define USB_OTG1_PHY_SW_Pxx_REQ BIT(2) +#define PCIE_PHY_SW_Pxx_REQ BIT(1) +#define MIPI_PHY_SW_Pxx_REQ BIT(0) + +#define GPC_M4_PU_PDN_FLG 0x1bc + + +#define PGC_MIPI 4 +#define PGC_PCIE 5 +#define PGC_USB_HSIC 8 +#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 imx7_pgc_domain { + struct generic_pm_domain genpd; + struct regmap *regmap; + struct regulator *regulator; + + unsigned int pgc; + + const struct { + u32 pxx; + u32 map; + } bits; + + const int voltage; + struct device *dev; +}; + +static int imx7_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, + bool on) +{ + struct imx7_pgc_domain *domain = container_of(genpd, + struct imx7_pgc_domain, + genpd); + unsigned int offset = on ? + GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ; + const bool enable_power_control = !on; + const bool has_regulator = !IS_ERR(domain->regulator); + unsigned long deadline; + int ret = 0; + + regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, + domain->bits.map, domain->bits.map); + + if (has_regulator && on) { + ret = regulator_enable(domain->regulator); + if (ret) { + dev_err(domain->dev, "failed to enable regulator\n"); + goto unmap; + } + } + + if (enable_power_control) + regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), + GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); + + regmap_update_bits(domain->regmap, offset, + 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 + */ + deadline = jiffies + msecs_to_jiffies(1); + while (true) { + u32 pxx_req; + + regmap_read(domain->regmap, offset, &pxx_req); + + if (!(pxx_req & domain->bits.pxx)) + break; + + if (time_after(jiffies, deadline)) { + dev_err(domain->dev, "falied to command PGC\n"); + ret = -ETIMEDOUT; + /* + * If we were in a process of enabling a + * domain and failed we might as well disable + * the regulator we just enabled. And if it + * was the opposite situation and we failed to + * power down -- keep the regulator on + */ + on = !on; + break; + } + + cpu_relax(); + } + + if (enable_power_control) + regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), + GPC_PGC_CTRL_PCR, 0); + + if (has_regulator && !on) { + int err; + + err = regulator_disable(domain->regulator); + if (err) + dev_err(domain->dev, + "failed to disable regulator: %d\n", ret); + /* Preserve earlier error code */ + ret = ret ?: err; + } +unmap: + regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, + domain->bits.map, 0); + return ret; +} + +static int imx7_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd) +{ + return imx7_gpc_pu_pgc_sw_pxx_req(genpd, true); +} + +static int imx7_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd) +{ + return imx7_gpc_pu_pgc_sw_pxx_req(genpd, false); +} + +static struct imx7_pgc_domain imx7_pgc_domains[] = { + [IMX7_POWER_DOMAIN_MIPI_PHY] = { + .genpd = { + .name = "mipi-phy", + }, + .bits = { + .pxx = MIPI_PHY_SW_Pxx_REQ, + .map = MIPI_PHY_A7_DOMAIN, + }, + .voltage = 1000000, + .pgc = PGC_MIPI, + }, + + [IMX7_POWER_DOMAIN_PCIE_PHY] = { + .genpd = { + .name = "pcie-phy", + }, + .bits = { + .pxx = PCIE_PHY_SW_Pxx_REQ, + .map = PCIE_PHY_A7_DOMAIN, + }, + .voltage = 1000000, + .pgc = PGC_PCIE, + }, + + [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { + .genpd = { + .name = "usb-hsic-phy", + }, + .bits = { + .pxx = USB_HSIC_PHY_SW_Pxx_REQ, + .map = USB_HSIC_PHY_A7_DOMAIN, + }, + .voltage = 1200000, + .pgc = PGC_USB_HSIC, + }, +}; + +static int imx7_pgc_domain_probe(struct platform_device *pdev) +{ + struct imx7_pgc_domain *domain = pdev->dev.platform_data; + int ret; + + domain->dev = &pdev->dev; + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err(domain->dev, "Failed to init power domain\n"); + return ret; + } + + domain->regulator = devm_regulator_get_optional(domain->dev, "power"); + if (IS_ERR(domain->regulator)) { + if (PTR_ERR(domain->regulator) != -ENODEV) { + dev_err(domain->dev, "Failed to get domain's regulator\n"); + return PTR_ERR(domain->regulator); + } + } else { + regulator_set_voltage(domain->regulator, + domain->voltage, domain->voltage); + } + + ret = of_genpd_add_provider_simple(domain->dev->of_node, + &domain->genpd); + if (ret) { + dev_err(domain->dev, "Failed to add genpd provider\n"); + pm_genpd_remove(&domain->genpd); + } + + return ret; +} + +static int imx7_pgc_domain_remove(struct platform_device *pdev) +{ + struct imx7_pgc_domain *domain = pdev->dev.platform_data; + + of_genpd_del_provider(domain->dev->of_node); + pm_genpd_remove(&domain->genpd); + + return 0; +} + +static const struct platform_device_id imx7_pgc_domain_id[] = { + { "imx7-pgc-domain", }, + { }, +}; + +static struct platform_driver imx7_pgc_domain_driver = { + .driver = { + .name = "imx7-pgc", + }, + .probe = imx7_pgc_domain_probe, + .remove = imx7_pgc_domain_remove, + .id_table = imx7_pgc_domain_id, +}; +builtin_platform_driver(imx7_pgc_domain_driver) + +static int imx_gpcv2_probe(struct platform_device *pdev) +{ + static const struct regmap_range yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A7_BSC, + GPC_M4_PU_PDN_FLG), + regmap_reg_range(GPC_PGC_CTRL(PGC_MIPI), + GPC_PGC_SR(PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(PGC_PCIE), + GPC_PGC_SR(PGC_PCIE)), + regmap_reg_range(GPC_PGC_CTRL(PGC_USB_HSIC), + GPC_PGC_SR(PGC_USB_HSIC)), + }; + static const struct regmap_access_table access_table = { + .yes_ranges = yes_ranges, + .n_yes_ranges = ARRAY_SIZE(yes_ranges), + }; + static const struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .rd_table = &access_table, + .wr_table = &access_table, + .max_register = SZ_4K, + }; + struct device *dev = &pdev->dev; + struct device_node *pgc_np, *np; + struct regmap *regmap; + struct resource *res; + 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; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, ®map_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 imx7_pgc_domain *domain; + u32 domain_index; + + 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 >= ARRAY_SIZE(imx7_pgc_domains)) { + dev_warn(dev, + "Domain index %d is out of bounds\n", + domain_index); + continue; + } + + domain = &imx7_pgc_domains[domain_index]; + domain->regmap = regmap; + domain->genpd.power_on = imx7_gpc_pu_pgc_sw_pup_req; + domain->genpd.power_off = imx7_gpc_pu_pgc_sw_pdn_req; + + pd_pdev = platform_device_alloc("imx7-pgc-domain", + domain_index); + if (!pd_pdev) { + dev_err(dev, "Failed to allocate platform device\n"); + of_node_put(np); + return -ENOMEM; + } + + pd_pdev->dev.platform_data = domain; + pd_pdev->dev.parent = dev; + pd_pdev->dev.of_node = 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" }, + { } +}; + +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) -- cgit v1.2.3 From 7c42af783ab817f40a8cfb9aef05b6fb92b780b3 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 5 Apr 2017 15:19:07 +0200 Subject: soc: imx: gpc: add defines for domain index Makes referencing a specfic domain in the driver code less error prone. Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 4294287e5f6c..d4ebb325b558 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -235,6 +235,10 @@ static struct platform_driver imx_pgc_power_domain_driver = { }; builtin_platform_driver(imx_pgc_power_domain_driver) +#define GPC_PGC_DOMAIN_ARM 0 +#define GPC_PGC_DOMAIN_PU 1 +#define GPC_PGC_DOMAIN_DISPLAY 2 + static struct genpd_power_state imx6_pm_domain_pu_state = { .power_off_latency_ns = 25000, .power_on_latency_ns = 2000000, @@ -340,7 +344,7 @@ static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap, genpd_err: for (i = 0; i < num_domains; i++) pm_genpd_remove(&imx_gpc_domains[i].base); - imx_pgc_put_clocks(&imx_gpc_domains[1]); + imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); clk_err: return ret; } @@ -441,12 +445,12 @@ static int imx_gpc_remove(struct platform_device *pdev) if (!of_get_child_by_name(pdev->dev.of_node, "pgc")) { of_genpd_del_provider(pdev->dev.of_node); - ret = pm_genpd_remove(&imx_gpc_domains[1].base); + ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base); if (ret) return ret; - imx_pgc_put_clocks(&imx_gpc_domains[1]); + imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); - ret = pm_genpd_remove(&imx_gpc_domains[0].base); + ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base); if (ret) return ret; } -- cgit v1.2.3 From 44c43c98213fb123819c67c128a5d6c9a9a12280 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 5 Apr 2017 15:19:09 +0200 Subject: soc: imx: gpc: add workaround for i.MX6QP to the GPC PD driver On i.MX6QP, due to hardware erratum ERR009619, the PRE clocks may be stalled during the power up sequencing of the PU power domain. As this may lead to a complete loss of display output, the recommended workaround is to keep the PU domain enabled during normal system operation. Implement this by rejecting the domain power down request on the affected SoC. Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index d4ebb325b558..47e7aa963dbb 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -36,6 +36,8 @@ #define GPC_CLK_MAX 6 +#define PGC_DOMAIN_FLAG_NO_PD BIT(0) + struct imx_pm_domain { struct generic_pm_domain base; struct regmap *regmap; @@ -45,6 +47,7 @@ struct imx_pm_domain { unsigned int reg_offs; signed char cntr_pdn_bit; unsigned int ipg_rate_mhz; + unsigned int flags; }; static inline struct imx_pm_domain * @@ -59,6 +62,9 @@ static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd) int iso, iso2sw; u32 val; + if (pd->flags & PGC_DOMAIN_FLAG_NO_PD) + return -EBUSY; + /* Read ISO and ISO2SW power down delays */ regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val); iso = val & 0x3f; @@ -272,18 +278,27 @@ static struct imx_pm_domain imx_gpc_domains[] = { struct imx_gpc_dt_data { int num_domains; + bool err009619_present; }; static const struct imx_gpc_dt_data imx6q_dt_data = { .num_domains = 2, + .err009619_present = false, +}; + +static const struct imx_gpc_dt_data imx6qp_dt_data = { + .num_domains = 2, + .err009619_present = true, }; static const struct imx_gpc_dt_data imx6sl_dt_data = { .num_domains = 3, + .err009619_present = false, }; static const struct of_device_id imx_gpc_dt_ids[] = { { .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data }, + { .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data }, { .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data }, { } }; @@ -381,6 +396,11 @@ static int imx_gpc_probe(struct platform_device *pdev) return ret; } + /* Disable PU power down in normal operation if ERR009619 is present */ + if (of_id_data->err009619_present) + imx_gpc_domains[GPC_PGC_DOMAIN_PU].flags |= + PGC_DOMAIN_FLAG_NO_PD; + if (!pgc_node) { ret = imx_gpc_old_dt_init(&pdev->dev, regmap, of_id_data->num_domains); -- cgit v1.2.3 From b1d134ba9de2b7a136406530e34fc8b110ba6efd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 31 Mar 2017 11:01:54 +0200 Subject: soc: renesas: Register SoC device early The r8a7795 SYSC driver manages PM Domains, and thus is initialized from an early_initcall(). However, this means the driver cannot check the SoC revision, as the SoC device hasn't been registered yet. Change renesas_soc_init() from a core_initcall() to an early_initcall() to fix this (renesas-soc.o is listed before rcar-sysc.o in the Makefile). Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 330960312296..51ed33f3426d 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -254,4 +254,4 @@ static int __init renesas_soc_init(void) return 0; } -core_initcall(renesas_soc_init); +early_initcall(renesas_soc_init); -- cgit v1.2.3 From afa6f53df6052968ce3934ad324777c0057e31d1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 31 Mar 2017 11:01:55 +0200 Subject: soc: renesas: rcar-sysc: Add support for fixing up power area tables The same SoC may have different power areas, depending on SoC revision. One option is to use different sets of power area tables for each SoC revision. However, if the differences are small, it is much more space-efficient to have a single set of tables, and fix those up at runtime instead. Hence provide a helper to NULLify power areas that do not exist on some revisions (NULLified power areas are skipped during the registration phase), and support for an optional initialization callback to e.g. fix up power area tables. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-sysc.c | 25 ++++++++++++++++++++++++- drivers/soc/renesas/rcar-sysc.h | 10 ++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 225c35c79d9a..528a13742aeb 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -2,7 +2,7 @@ * R-Car SYSC Power management support * * Copyright (C) 2014 Magnus Damm - * Copyright (C) 2015-2016 Glider bvba + * Copyright (C) 2015-2017 Glider bvba * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -334,6 +334,12 @@ static int __init rcar_sysc_pd_init(void) info = match->data; + if (info->init) { + error = info->init(); + if (error) + return error; + } + has_cpg_mstp = of_find_compatible_node(NULL, NULL, "renesas,cpg-mstp-clocks"); @@ -377,6 +383,11 @@ static int __init rcar_sysc_pd_init(void) const struct rcar_sysc_area *area = &info->areas[i]; struct rcar_sysc_pd *pd; + if (!area->name) { + /* Skip NULLified area */ + continue; + } + pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL); if (!pd) { error = -ENOMEM; @@ -406,6 +417,18 @@ out_put: } early_initcall(rcar_sysc_pd_init); +void __init rcar_sysc_nullify(struct rcar_sysc_area *areas, + unsigned int num_areas, u8 id) +{ + unsigned int i; + + for (i = 0; i < num_areas; i++) + if (areas[i].isr_bit == id) { + areas[i].name = NULL; + return; + } +} + void __init rcar_sysc_init(phys_addr_t base, u32 syscier) { u32 syscimr; diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index f6e842e2976e..07edb049a401 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -46,6 +46,7 @@ struct rcar_sysc_area { */ struct rcar_sysc_info { + int (*init)(void); /* Optional */ const struct rcar_sysc_area *areas; unsigned int num_areas; }; @@ -59,4 +60,13 @@ extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern const struct rcar_sysc_info r8a7795_sysc_info; extern const struct rcar_sysc_info r8a7796_sysc_info; + + + /* + * Helpers for fixing up power area tables depending on SoC revision + */ + +extern void rcar_sysc_nullify(struct rcar_sysc_area *areas, + unsigned int num_areas, u8 id); + #endif /* __SOC_RENESAS_RCAR_SYSC_H__ */ -- cgit v1.2.3 From fcb87087261e1be51b4c03677f39246bdc312b1c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 31 Mar 2017 11:01:56 +0200 Subject: soc: renesas: rcar-sysc: Add support for R-Car H3 ES2.0 Power area A2VC0 was removed in revision ES2.0, cfr. R-Car Gen3 Hardware User's Manual rev. 0.53E. Hence remove it from the power area table when not running on ES1.x. This is in line with the goal to: 1. Support both the ES1.x and ES2.0 SoC revisions in a single binary for now, 2. Make it clear which code supports ES1.x, so it can easily be identified and removed later, when production SoCs are deemed ubiquitous. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/r8a7795-sysc.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a7795-sysc.c b/drivers/soc/renesas/r8a7795-sysc.c index 5e7537c96f7b..7412666187b3 100644 --- a/drivers/soc/renesas/r8a7795-sysc.c +++ b/drivers/soc/renesas/r8a7795-sysc.c @@ -1,7 +1,7 @@ /* * Renesas R-Car H3 System Controller * - * Copyright (C) 2016 Glider bvba + * Copyright (C) 2016-2017 Glider bvba * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -10,12 +10,13 @@ #include #include +#include #include #include "rcar-sysc.h" -static const struct rcar_sysc_area r8a7795_areas[] __initconst = { +static struct rcar_sysc_area r8a7795_areas[] __initdata = { { "always-on", 0, 0, R8A7795_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, { "ca57-scu", 0x1c0, 0, R8A7795_PD_CA57_SCU, R8A7795_PD_ALWAYS_ON, PD_SCU }, @@ -40,6 +41,7 @@ static const struct rcar_sysc_area r8a7795_areas[] __initconst = { { "a3vp", 0x340, 0, R8A7795_PD_A3VP, R8A7795_PD_ALWAYS_ON }, { "cr7", 0x240, 0, R8A7795_PD_CR7, R8A7795_PD_ALWAYS_ON }, { "a3vc", 0x380, 0, R8A7795_PD_A3VC, R8A7795_PD_ALWAYS_ON }, + /* A2VC0 exists on ES1.x only */ { "a2vc0", 0x3c0, 0, R8A7795_PD_A2VC0, R8A7795_PD_A3VC }, { "a2vc1", 0x3c0, 1, R8A7795_PD_A2VC1, R8A7795_PD_A3VC }, { "3dg-a", 0x100, 0, R8A7795_PD_3DG_A, R8A7795_PD_ALWAYS_ON }, @@ -50,7 +52,27 @@ static const struct rcar_sysc_area r8a7795_areas[] __initconst = { { "a3ir", 0x180, 0, R8A7795_PD_A3IR, R8A7795_PD_ALWAYS_ON }, }; + + /* + * Fixups for R-Car H3 revisions after ES1.x + */ + +static const struct soc_device_attribute r8a7795es1[] __initconst = { + { .soc_id = "r8a7795", .revision = "ES1.*" }, + { /* sentinel */ } +}; + +static int __init r8a7795_sysc_init(void) +{ + if (!soc_device_match(r8a7795es1)) + rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas), + R8A7795_PD_A2VC0); + + return 0; +} + const struct rcar_sysc_info r8a7795_sysc_info __initconst = { + .init = r8a7795_sysc_init, .areas = r8a7795_areas, .num_areas = ARRAY_SIZE(r8a7795_areas), }; -- cgit v1.2.3 From 7574f67eb457bbd053e5addae665c187f309668b Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Mon, 24 Apr 2017 14:17:35 -0700 Subject: soc: pm-domain: Fix the mangled urls Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/ti_sci_pm_domains.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index d9dccb0c3a2a..b0b283810e72 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -1,7 +1,7 @@ /* * TI SCI Generic Power Domain Driver * - * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://urldefense.proofpoint.com/v2/url?u=http-3A__www.ti.com_&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PQcxBKCX5YTpkKY057SbK10&r=XBn1JQGPwR8CsE7xpP3wPlG6DQU7qw8ym65xieNZ4hY&m=R6qGiR9DbG1C3EF_0mL-m-qkmSO64GklbFWpUzqt8fY&s=YTWcQCWi5lnIf4XHDLq1XDd4JbZv9xpqOwdPD8xEdZE&e= + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ * J Keerthy * Dave Gerlach * -- cgit v1.2.3