[PATCH v3 23/25] soc: actions: Add Owl SPS
Implement S500 Smart Power System power-gating. Based on LeMaker linux-actions tree. Signed-off-by: Andreas Färber--- v3: new drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/actions/Kconfig | 12 +++ drivers/soc/actions/Makefile | 1 + drivers/soc/actions/owl-sps.c | 245 ++ 5 files changed, 260 insertions(+) create mode 100644 drivers/soc/actions/Kconfig create mode 100644 drivers/soc/actions/Makefile create mode 100644 drivers/soc/actions/owl-sps.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 45e5b13..afd3c43 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,5 +1,6 @@ menu "SOC (System On Chip) specific Drivers" +source "drivers/soc/actions/Kconfig" source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 3467de7..a934480 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -2,6 +2,7 @@ # Makefile for the Linux Kernel SOC specific device drivers. # +obj-$(CONFIG_ARCH_ACTIONS) += actions/ obj-$(CONFIG_ARCH_AT91)+= atmel/ obj-y += bcm/ obj-$(CONFIG_ARCH_DOVE)+= dove/ diff --git a/drivers/soc/actions/Kconfig b/drivers/soc/actions/Kconfig new file mode 100644 index 000..f877f7d --- /dev/null +++ b/drivers/soc/actions/Kconfig @@ -0,0 +1,12 @@ +if ARCH_ACTIONS || COMPILE_TEST + +config OWL_PM_DOMAINS + bool "Actions Semi SPS power domains" + depends on PM +select PM_GENERIC_DOMAINS +help + Say 'y' here to enable support for Smart Power System (SPS) + power-gating on Actions Semiconductor S500 SoC. + If unsure, say 'n'. + +endif diff --git a/drivers/soc/actions/Makefile b/drivers/soc/actions/Makefile new file mode 100644 index 000..720c34e --- /dev/null +++ b/drivers/soc/actions/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c new file mode 100644 index 000..caf777e --- /dev/null +++ b/drivers/soc/actions/owl-sps.c @@ -0,0 +1,245 @@ +/* + * Actions Semi Owl Smart Power System (SPS) + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 Andreas Färber + * + * 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 the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#define OWL_SPS_PG_CTL 0x0 + +struct owl_sps_domain_info { + const char *name; + int pwr_bit; + int ack_bit; +}; + +struct owl_sps_info { + unsigned num_domains; + const struct owl_sps_domain_info *domains; +}; + +struct owl_sps { + struct device *dev; + const struct owl_sps_info *info; + void __iomem *base; + struct genpd_onecell_data genpd_data; + struct generic_pm_domain *domains[]; +}; + +#define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) + +struct owl_sps_domain { + struct generic_pm_domain genpd; + const struct owl_sps_domain_info *info; + struct owl_sps *sps; +}; + +static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) +{ + u32 val, pwr_mask, ack_mask; + int timeout; + bool ack; + + ack_mask = BIT(pd->info->ack_bit); + pwr_mask = BIT(pd->info->pwr_bit); + val = readl(pd->sps->base + OWL_SPS_PG_CTL); + ack = val & ack_mask; + + if (ack == enable) + return 0; + + if (enable) + val |= pwr_mask; + else + val &= ~pwr_mask; + + writel(val, pd->sps->base + OWL_SPS_PG_CTL); + + for (timeout = 5000; timeout > 0; timeout -= 50) { + val = readl(pd->sps->base + OWL_SPS_PG_CTL); + if ((val & ack_mask) == (enable ? ack_mask : 0)) + break; + udelay(50); + } + if (timeout <= 0) + return -ETIMEDOUT; + + udelay(10); + + return 0; +} + +static int owl_sps_power_on(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + return owl_sps_set_power(pd, true); +} + +static int owl_sps_power_off(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + return owl_sps_set_power(pd, false); +} + +static int owl_sps_init_domain(struct owl_sps *sps, int index) +{ + struct owl_sps_domain *pd; + + pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->info = >info->domains[index]; + pd->sps = sps; + + pd->genpd.name =
[PATCH v3 23/25] soc: actions: Add Owl SPS
Implement S500 Smart Power System power-gating. Based on LeMaker linux-actions tree. Signed-off-by: Andreas Färber --- v3: new drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/actions/Kconfig | 12 +++ drivers/soc/actions/Makefile | 1 + drivers/soc/actions/owl-sps.c | 245 ++ 5 files changed, 260 insertions(+) create mode 100644 drivers/soc/actions/Kconfig create mode 100644 drivers/soc/actions/Makefile create mode 100644 drivers/soc/actions/owl-sps.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 45e5b13..afd3c43 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,5 +1,6 @@ menu "SOC (System On Chip) specific Drivers" +source "drivers/soc/actions/Kconfig" source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 3467de7..a934480 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -2,6 +2,7 @@ # Makefile for the Linux Kernel SOC specific device drivers. # +obj-$(CONFIG_ARCH_ACTIONS) += actions/ obj-$(CONFIG_ARCH_AT91)+= atmel/ obj-y += bcm/ obj-$(CONFIG_ARCH_DOVE)+= dove/ diff --git a/drivers/soc/actions/Kconfig b/drivers/soc/actions/Kconfig new file mode 100644 index 000..f877f7d --- /dev/null +++ b/drivers/soc/actions/Kconfig @@ -0,0 +1,12 @@ +if ARCH_ACTIONS || COMPILE_TEST + +config OWL_PM_DOMAINS + bool "Actions Semi SPS power domains" + depends on PM +select PM_GENERIC_DOMAINS +help + Say 'y' here to enable support for Smart Power System (SPS) + power-gating on Actions Semiconductor S500 SoC. + If unsure, say 'n'. + +endif diff --git a/drivers/soc/actions/Makefile b/drivers/soc/actions/Makefile new file mode 100644 index 000..720c34e --- /dev/null +++ b/drivers/soc/actions/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c new file mode 100644 index 000..caf777e --- /dev/null +++ b/drivers/soc/actions/owl-sps.c @@ -0,0 +1,245 @@ +/* + * Actions Semi Owl Smart Power System (SPS) + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 Andreas Färber + * + * 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 the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#define OWL_SPS_PG_CTL 0x0 + +struct owl_sps_domain_info { + const char *name; + int pwr_bit; + int ack_bit; +}; + +struct owl_sps_info { + unsigned num_domains; + const struct owl_sps_domain_info *domains; +}; + +struct owl_sps { + struct device *dev; + const struct owl_sps_info *info; + void __iomem *base; + struct genpd_onecell_data genpd_data; + struct generic_pm_domain *domains[]; +}; + +#define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) + +struct owl_sps_domain { + struct generic_pm_domain genpd; + const struct owl_sps_domain_info *info; + struct owl_sps *sps; +}; + +static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) +{ + u32 val, pwr_mask, ack_mask; + int timeout; + bool ack; + + ack_mask = BIT(pd->info->ack_bit); + pwr_mask = BIT(pd->info->pwr_bit); + val = readl(pd->sps->base + OWL_SPS_PG_CTL); + ack = val & ack_mask; + + if (ack == enable) + return 0; + + if (enable) + val |= pwr_mask; + else + val &= ~pwr_mask; + + writel(val, pd->sps->base + OWL_SPS_PG_CTL); + + for (timeout = 5000; timeout > 0; timeout -= 50) { + val = readl(pd->sps->base + OWL_SPS_PG_CTL); + if ((val & ack_mask) == (enable ? ack_mask : 0)) + break; + udelay(50); + } + if (timeout <= 0) + return -ETIMEDOUT; + + udelay(10); + + return 0; +} + +static int owl_sps_power_on(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + return owl_sps_set_power(pd, true); +} + +static int owl_sps_power_off(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + return owl_sps_set_power(pd, false); +} + +static int owl_sps_init_domain(struct owl_sps *sps, int index) +{ + struct owl_sps_domain *pd; + + pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->info = >info->domains[index]; + pd->sps = sps; + + pd->genpd.name = pd->info->name; +