From 58cbff023bfaeb9c290b5dbcc0a4bb327c653e18 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 2 Jul 2020 08:45:09 -0700 Subject: soc: ti: omap-prm: Add basic power domain support The PRM controller has currently only support for resets while the power domains are still handled in the platform code. Let's add basic power domain support to enable and disable a PRM controlled power domain if configured in the devicetree. This can be used for various hardware accelerators, and interconnect instances. Further support can be added later on as needed for runtime configuration based on domain-idle-states. Signed-off-by: Tony Lindgren Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- drivers/soc/ti/omap_prm.c | 228 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 227 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index c9b3f9ebf0bb..819921b6f925 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -10,14 +10,39 @@ #include #include #include +#include #include #include #include +#include #include #include #include +enum omap_prm_domain_mode { + OMAP_PRMD_OFF, + OMAP_PRMD_RETENTION, + OMAP_PRMD_ON_INACTIVE, + OMAP_PRMD_ON_ACTIVE, +}; + +struct omap_prm_domain_map { + unsigned int usable_modes; /* Mask of hardware supported modes */ + unsigned long statechange:1; /* Optional low-power state change */ + unsigned long logicretstate:1; /* Optional logic off mode */ +}; + +struct omap_prm_domain { + struct device *dev; + struct omap_prm *prm; + struct generic_pm_domain pd; + u16 pwrstctrl; + u16 pwrstst; + const struct omap_prm_domain_map *cap; + u32 pwrstctrl_saved; +}; + struct omap_rst_map { s8 rst; s8 st; @@ -27,6 +52,9 @@ struct omap_prm_data { u32 base; const char *name; const char *clkdm_name; + u16 pwrstctrl; + u16 pwrstst; + const struct omap_prm_domain_map *dmap; u16 rstctrl; u16 rstst; const struct omap_rst_map *rstmap; @@ -36,6 +64,7 @@ struct omap_prm_data { struct omap_prm { const struct omap_prm_data *data; void __iomem *base; + struct omap_prm_domain *prmd; }; struct omap_reset_data { @@ -47,6 +76,7 @@ struct omap_reset_data { struct device *dev; }; +#define genpd_to_prm_domain(gpd) container_of(gpd, struct omap_prm_domain, pd) #define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) #define OMAP_MAX_RESETS 8 @@ -58,6 +88,13 @@ struct omap_reset_data { #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) +#define PRM_STATE_MAX_WAIT 10000 +#define PRM_LOGICRETSTATE BIT(2) +#define PRM_LOWPOWERSTATECHANGE BIT(4) +#define PRM_POWERSTATE_MASK OMAP_PRMD_ON_ACTIVE + +#define PRM_ST_INTRANSITION BIT(20) + static const struct omap_rst_map rst_map_0[] = { { .rst = 0, .st = 0 }, { .rst = -1 }, @@ -151,6 +188,180 @@ static const struct of_device_id omap_prm_id_table[] = { { }, }; +#ifdef DEBUG +static void omap_prm_domain_show_state(struct omap_prm_domain *prmd, + const char *desc) +{ + dev_dbg(prmd->dev, "%s %s: %08x/%08x\n", + prmd->pd.name, desc, + readl_relaxed(prmd->prm->base + prmd->pwrstctrl), + readl_relaxed(prmd->prm->base + prmd->pwrstst)); +} +#else +static inline void omap_prm_domain_show_state(struct omap_prm_domain *prmd, + const char *desc) +{ +} +#endif + +static int omap_prm_domain_power_on(struct generic_pm_domain *domain) +{ + struct omap_prm_domain *prmd; + int ret; + u32 v; + + prmd = genpd_to_prm_domain(domain); + if (!prmd->cap) + return 0; + + omap_prm_domain_show_state(prmd, "on: previous state"); + + if (prmd->pwrstctrl_saved) + v = prmd->pwrstctrl_saved; + else + v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); + + writel_relaxed(v | OMAP_PRMD_ON_ACTIVE, + prmd->prm->base + prmd->pwrstctrl); + + /* wait for the transition bit to get cleared */ + ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, + v, !(v & PRM_ST_INTRANSITION), 1, + PRM_STATE_MAX_WAIT); + if (ret) + dev_err(prmd->dev, "%s: %s timed out\n", + prmd->pd.name, __func__); + + omap_prm_domain_show_state(prmd, "on: new state"); + + return ret; +} + +/* No need to check for holes in the mask for the lowest mode */ +static int omap_prm_domain_find_lowest(struct omap_prm_domain *prmd) +{ + return __ffs(prmd->cap->usable_modes); +} + +static int omap_prm_domain_power_off(struct generic_pm_domain *domain) +{ + struct omap_prm_domain *prmd; + int ret; + u32 v; + + prmd = genpd_to_prm_domain(domain); + if (!prmd->cap) + return 0; + + omap_prm_domain_show_state(prmd, "off: previous state"); + + v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); + prmd->pwrstctrl_saved = v; + + v &= ~PRM_POWERSTATE_MASK; + v |= omap_prm_domain_find_lowest(prmd); + + if (prmd->cap->statechange) + v |= PRM_LOWPOWERSTATECHANGE; + if (prmd->cap->logicretstate) + v &= ~PRM_LOGICRETSTATE; + else + v |= PRM_LOGICRETSTATE; + + writel_relaxed(v, prmd->prm->base + prmd->pwrstctrl); + + /* wait for the transition bit to get cleared */ + ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, + v, !(v & PRM_ST_INTRANSITION), 1, + PRM_STATE_MAX_WAIT); + if (ret) + dev_warn(prmd->dev, "%s: %s timed out\n", + __func__, prmd->pd.name); + + omap_prm_domain_show_state(prmd, "off: new state"); + + return 0; +} + +static int omap_prm_domain_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct generic_pm_domain_data *genpd_data; + struct of_phandle_args pd_args; + struct omap_prm_domain *prmd; + struct device_node *np; + int ret; + + prmd = genpd_to_prm_domain(domain); + np = dev->of_node; + + 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 != 0) + dev_warn(dev, "%s: unusupported #power-domain-cells: %i\n", + prmd->pd.name, pd_args.args_count); + + genpd_data = dev_gpd_data(dev); + genpd_data->data = NULL; + + return 0; +} + +static void omap_prm_domain_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct generic_pm_domain_data *genpd_data; + + genpd_data = dev_gpd_data(dev); + genpd_data->data = NULL; +} + +static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm) +{ + struct omap_prm_domain *prmd; + struct device_node *np = dev->of_node; + const struct omap_prm_data *data; + const char *name; + int error; + + if (!of_find_property(dev->of_node, "#power-domain-cells", NULL)) + return 0; + + of_node_put(dev->of_node); + + prmd = devm_kzalloc(dev, sizeof(*prmd), GFP_KERNEL); + if (!prmd) + return -ENOMEM; + + data = prm->data; + name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s", + data->name); + + prmd->dev = dev; + prmd->prm = prm; + prmd->cap = prmd->prm->data->dmap; + prmd->pwrstctrl = prmd->prm->data->pwrstctrl; + prmd->pwrstst = prmd->prm->data->pwrstst; + + prmd->pd.name = name; + prmd->pd.power_on = omap_prm_domain_power_on; + prmd->pd.power_off = omap_prm_domain_power_off; + prmd->pd.attach_dev = omap_prm_domain_attach_dev; + prmd->pd.detach_dev = omap_prm_domain_detach_dev; + + pm_genpd_init(&prmd->pd, NULL, true); + error = of_genpd_add_provider_simple(np, &prmd->pd); + if (error) + pm_genpd_remove(&prmd->pd); + else + prm->prmd = prmd; + + return error; +} + static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id) { if (reset->mask & BIT(id)) @@ -351,6 +562,7 @@ static int omap_prm_probe(struct platform_device *pdev) const struct omap_prm_data *data; struct omap_prm *prm; const struct of_device_id *match; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -378,7 +590,21 @@ static int omap_prm_probe(struct platform_device *pdev) if (IS_ERR(prm->base)) return PTR_ERR(prm->base); - return omap_prm_reset_init(pdev, prm); + ret = omap_prm_domain_init(&pdev->dev, prm); + if (ret) + return ret; + + ret = omap_prm_reset_init(pdev, prm); + if (ret) + goto err_domain; + + return 0; + +err_domain: + of_genpd_del_provider(pdev->dev.of_node); + pm_genpd_remove(&prm->prmd->pd); + + return ret; } static struct platform_driver omap_prm_driver = { -- cgit v1.2.3 From f8f91486e8e717b71a5a3d6bb51c7ed751d83543 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 2 Jul 2020 08:45:10 -0700 Subject: soc: ti: omap-prm: Configure sgx power domain for am3 and am4 Let's configure only sgx power domain for am3 and am4 to start with. Signed-off-by: Tony Lindgren Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- drivers/soc/ti/omap_prm.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index 819921b6f925..4a5e5c6a31c4 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -95,6 +95,18 @@ struct omap_reset_data { #define PRM_ST_INTRANSITION BIT(20) +static const struct omap_prm_domain_map omap_prm_noinact = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) | + BIT(OMAP_PRMD_OFF), + .statechange = 1, + .logicretstate = 1, +}; + +static const struct omap_prm_domain_map omap_prm_onoff_noauto = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF), + .statechange = 1, +}; + static const struct omap_rst_map rst_map_0[] = { { .rst = 0, .st = 0 }, { .rst = -1 }, @@ -156,7 +168,11 @@ static const struct omap_prm_data am3_prm_data[] = { { .name = "per", .base = 0x44e00c00, .rstctrl = 0x0, .rstmap = am3_per_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" }, { .name = "wkup", .base = 0x44e00d00, .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, { .name = "device", .base = 0x44e00f00, .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, - { .name = "gfx", .base = 0x44e01100, .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3" }, + { + .name = "gfx", .base = 0x44e01100, + .pwrstctrl = 0, .pwrstst = 0x10, .dmap = &omap_prm_noinact, + .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", + }, { }, }; @@ -172,7 +188,11 @@ static const struct omap_rst_map am4_device_rst_map[] = { }; static const struct omap_prm_data am4_prm_data[] = { - { .name = "gfx", .base = 0x44df0400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3" }, + { + .name = "gfx", .base = 0x44df0400, + .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", + }, { .name = "per", .base = 0x44df0800, .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map, .clkdm_name = "pruss_ocp" }, { .name = "wkup", .base = 0x44df2000, .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map, .flags = OMAP_PRM_HAS_NO_CLKDM }, { .name = "device", .base = 0x44df4000, .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, -- cgit v1.2.3 From 2bbcd6590a25336b0963d8678488e0a2639260a3 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 2 Jul 2020 08:45:11 -0700 Subject: soc: ti: omap-prm: Configure omap4 and 5 l4_abe power domain Let's add omap4 and 5 l4_abe interconnect instance for the power domain. Signed-off-by: Tony Lindgren Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- drivers/soc/ti/omap_prm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index 4a5e5c6a31c4..980b04c38fd9 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -95,6 +95,13 @@ struct omap_reset_data { #define PRM_ST_INTRANSITION BIT(20) +static const struct omap_prm_domain_map omap_prm_all = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | + BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF), + .statechange = 1, + .logicretstate = 1, +}; + static const struct omap_prm_domain_map omap_prm_noinact = { .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF), @@ -102,6 +109,13 @@ static const struct omap_prm_domain_map omap_prm_noinact = { .logicretstate = 1, }; +static const struct omap_prm_domain_map omap_prm_nooff = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | + BIT(OMAP_PRMD_RETENTION), + .statechange = 1, + .logicretstate = 1, +}; + static const struct omap_prm_domain_map omap_prm_onoff_noauto = { .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF), .statechange = 1, @@ -127,6 +141,10 @@ static const struct omap_rst_map rst_map_012[] = { static const struct omap_prm_data omap4_prm_data[] = { { .name = "tesla", .base = 0x4a306400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { + .name = "abe", .base = 0x4a306500, + .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_all, + }, { .name = "core", .base = 0x4a306700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati", .rstmap = rst_map_012 }, { .name = "ivahd", .base = 0x4a306f00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, { .name = "device", .base = 0x4a307b00, .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, @@ -135,6 +153,10 @@ static const struct omap_prm_data omap4_prm_data[] = { static const struct omap_prm_data omap5_prm_data[] = { { .name = "dsp", .base = 0x4ae06400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { + .name = "abe", .base = 0x4ae06500, + .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_nooff, + }, { .name = "core", .base = 0x4ae06700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu", .rstmap = rst_map_012 }, { .name = "iva", .base = 0x4ae07200, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, { .name = "device", .base = 0x4ae07c00, .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, -- cgit v1.2.3 From 17132da70eb766785b9b4677bacce18cc11ea442 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2020 20:20:33 +0200 Subject: ARM: samsung: move pm check code to drivers/soc This is the only part of plat-samsung that is really shared between the s3c and s5p ports. Moving it to drivers/soc/ lets us make them completely independent. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200806182059.2431-16-krzk@kernel.org Signed-off-by: Krzysztof Kozlowski --- arch/arm/mach-s5pv210/Kconfig | 1 + arch/arm/plat-samsung/Kconfig | 49 +----- arch/arm/plat-samsung/Makefile | 2 - arch/arm/plat-samsung/include/plat/pm-common.h | 70 +------- arch/arm/plat-samsung/pm-check.c | 233 ------------------------- arch/arm/plat-samsung/pm-debug.c | 80 --------- drivers/soc/samsung/Kconfig | 48 ++++- drivers/soc/samsung/Makefile | 3 + drivers/soc/samsung/s3c-pm-check.c | 233 +++++++++++++++++++++++++ drivers/soc/samsung/s3c-pm-debug.c | 79 +++++++++ include/linux/soc/samsung/s3c-pm.h | 84 +++++++++ 11 files changed, 449 insertions(+), 433 deletions(-) delete mode 100644 arch/arm/plat-samsung/pm-check.c delete mode 100644 arch/arm/plat-samsung/pm-debug.c create mode 100644 drivers/soc/samsung/s3c-pm-check.c create mode 100644 drivers/soc/samsung/s3c-pm-debug.c create mode 100644 include/linux/soc/samsung/s3c-pm.h (limited to 'drivers/soc') diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig index b3db1191e437..95d4e8284866 100644 --- a/arch/arm/mach-s5pv210/Kconfig +++ b/arch/arm/mach-s5pv210/Kconfig @@ -17,6 +17,7 @@ config ARCH_S5PV210 select HAVE_S3C_RTC if RTC_CLASS select PINCTRL select PINCTRL_EXYNOS + select SOC_SAMSUNG help Samsung S5PV210/S5PC110 series based systems diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 920931c3fefd..a7cb0db1d5cc 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -8,6 +8,7 @@ config PLAT_SAMSUNG default y select GENERIC_IRQ_CHIP select NO_IOPORT_MAP + select SOC_SAMSUNG help Base platform code for all Samsung SoC based systems @@ -234,54 +235,6 @@ config SAMSUNG_PM_GPIO pinctrl-samsung driver. endif -comment "Power management" - -config SAMSUNG_PM_DEBUG - bool "Samsung PM Suspend debug" - depends on PM && DEBUG_KERNEL - depends on PLAT_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 - depends on DEBUG_S3C24XX_UART || DEBUG_S3C2410_UART - help - Say Y here if you want verbose debugging from the PM Suspend and - Resume code. See - for more information. - -config S3C_PM_DEBUG_LED_SMDK - bool "SMDK LED suspend/resume debugging" - depends on PM && (MACH_SMDK6410) - help - Say Y here to enable the use of the SMDK LEDs on the baseboard - for debugging of the state of the suspend and resume process. - - Note, this currently only works for S3C64XX based SMDK boards. - -config SAMSUNG_PM_CHECK - bool "S3C2410 PM Suspend Memory CRC" - depends on PM && (PLAT_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210) - select CRC32 - help - Enable the PM code's memory area checksum over sleep. This option - will generate CRCs of all blocks of memory, and store them before - going to sleep. The blocks are then checked on resume for any - errors. - - Note, this can take several seconds depending on memory size - and CPU speed. - - See - -config SAMSUNG_PM_CHECK_CHUNKSIZE - int "S3C2410 PM Suspend CRC Chunksize (KiB)" - depends on PM && SAMSUNG_PM_CHECK - default 64 - help - Set the chunksize in Kilobytes of the CRC for checking memory - corruption over suspend and resume. A smaller value will mean that - the CRC data block will take more memory, but will identify any - faults with better precision. - - See - config SAMSUNG_WAKEMASK bool depends on PM diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index d8a80fbcebb3..c47f58ed83e2 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -27,7 +27,5 @@ obj-$(CONFIG_GPIO_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_PM_SLEEP) += pm-common.o obj-$(CONFIG_SAMSUNG_PM) += pm.o obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o -obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o -obj-$(CONFIG_SAMSUNG_PM_DEBUG) += pm-debug.o obj-$(CONFIG_SAMSUNG_WAKEMASK) += wakeup-mask.o diff --git a/arch/arm/plat-samsung/include/plat/pm-common.h b/arch/arm/plat-samsung/include/plat/pm-common.h index 87fa97fd6e8b..18b9607e1e39 100644 --- a/arch/arm/plat-samsung/include/plat/pm-common.h +++ b/arch/arm/plat-samsung/include/plat/pm-common.h @@ -11,6 +11,7 @@ #define __PLAT_SAMSUNG_PM_COMMON_H __FILE__ #include +#include /* sleep save info */ @@ -36,73 +37,4 @@ extern void s3c_pm_do_save(struct sleep_save *ptr, int count); extern void s3c_pm_do_restore(const struct sleep_save *ptr, int count); extern void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count); -/* PM debug functions */ - -/** - * struct pm_uart_save - save block for core UART - * @ulcon: Save value for S3C2410_ULCON - * @ucon: Save value for S3C2410_UCON - * @ufcon: Save value for S3C2410_UFCON - * @umcon: Save value for S3C2410_UMCON - * @ubrdiv: Save value for S3C2410_UBRDIV - * - * Save block for UART registers to be held over sleep and restored if they - * are needed (say by debug). -*/ -struct pm_uart_save { - u32 ulcon; - u32 ucon; - u32 ufcon; - u32 umcon; - u32 ubrdiv; - u32 udivslot; -}; - -#ifdef CONFIG_SAMSUNG_PM_DEBUG -/** - * s3c_pm_dbg() - low level debug function for use in suspend/resume. - * @msg: The message to print. - * - * This function is used mainly to debug the resume process before the system - * can rely on printk/console output. It uses the low-level debugging output - * routine printascii() to do its work. - */ -extern void s3c_pm_dbg(const char *msg, ...); - -#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt) - -extern void s3c_pm_save_uarts(bool is_s3c24xx); -extern void s3c_pm_restore_uarts(bool is_s3c24xx); - -#ifdef CONFIG_ARCH_S3C64XX -extern void s3c_pm_arch_update_uart(void __iomem *regs, - struct pm_uart_save *save); -#else -static inline void -s3c_pm_arch_update_uart(void __iomem *regs, struct pm_uart_save *save) -{ -} -#endif - -#else -#define S3C_PMDBG(fmt...) pr_debug(fmt) - -static inline void s3c_pm_save_uarts(bool is_s3c24xx) { } -static inline void s3c_pm_restore_uarts(bool is_s3c24xx) { } -#endif - -/* suspend memory checking */ - -#ifdef CONFIG_SAMSUNG_PM_CHECK -extern void s3c_pm_check_prepare(void); -extern void s3c_pm_check_restore(void); -extern void s3c_pm_check_cleanup(void); -extern void s3c_pm_check_store(void); -#else -#define s3c_pm_check_prepare() do { } while (0) -#define s3c_pm_check_restore() do { } while (0) -#define s3c_pm_check_cleanup() do { } while (0) -#define s3c_pm_check_store() do { } while (0) -#endif - #endif diff --git a/arch/arm/plat-samsung/pm-check.c b/arch/arm/plat-samsung/pm-check.c deleted file mode 100644 index cd2c02c68bc3..000000000000 --- a/arch/arm/plat-samsung/pm-check.c +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// originally in linux/arch/arm/plat-s3c24xx/pm.c -// -// Copyright (c) 2004-2008 Simtec Electronics -// http://armlinux.simtec.co.uk -// Ben Dooks -// -// S3C Power Mangament - suspend/resume memory corruption check. - -#include -#include -#include -#include -#include -#include - -#include - -#if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1 -#error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value -#endif - -/* suspend checking code... - * - * this next area does a set of crc checks over all the installed - * memory, so the system can verify if the resume was ok. - * - * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, - * increasing it will mean that the area corrupted will be less easy to spot, - * and reducing the size will cause the CRC save area to grow -*/ - -#define CHECK_CHUNKSIZE (CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024) - -static u32 crc_size; /* size needed for the crc block */ -static u32 *crcs; /* allocated over suspend/resume */ - -typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); - -/* s3c_pm_run_res - * - * go through the given resource list, and look for system ram -*/ - -static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) -{ - while (ptr != NULL) { - if (ptr->child != NULL) - s3c_pm_run_res(ptr->child, fn, arg); - - if ((ptr->flags & IORESOURCE_SYSTEM_RAM) - == IORESOURCE_SYSTEM_RAM) { - S3C_PMDBG("Found system RAM at %08lx..%08lx\n", - (unsigned long)ptr->start, - (unsigned long)ptr->end); - arg = (fn)(ptr, arg); - } - - ptr = ptr->sibling; - } -} - -static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) -{ - s3c_pm_run_res(&iomem_resource, fn, arg); -} - -static u32 *s3c_pm_countram(struct resource *res, u32 *val) -{ - u32 size = (u32)resource_size(res); - - size += CHECK_CHUNKSIZE-1; - size /= CHECK_CHUNKSIZE; - - S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", - (unsigned long)res->start, (unsigned long)res->end, size); - - *val += size * sizeof(u32); - return val; -} - -/* s3c_pm_prepare_check - * - * prepare the necessary information for creating the CRCs. This - * must be done before the final save, as it will require memory - * allocating, and thus touching bits of the kernel we do not - * know about. -*/ - -void s3c_pm_check_prepare(void) -{ - crc_size = 0; - - s3c_pm_run_sysram(s3c_pm_countram, &crc_size); - - S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size); - - crcs = kmalloc(crc_size+4, GFP_KERNEL); - if (crcs == NULL) - printk(KERN_ERR "Cannot allocated CRC save area\n"); -} - -static u32 *s3c_pm_makecheck(struct resource *res, u32 *val) -{ - unsigned long addr, left; - - for (addr = res->start; addr < res->end; - addr += CHECK_CHUNKSIZE) { - left = res->end - addr; - - if (left > CHECK_CHUNKSIZE) - left = CHECK_CHUNKSIZE; - - *val = crc32_le(~0, phys_to_virt(addr), left); - val++; - } - - return val; -} - -/* s3c_pm_check_store - * - * compute the CRC values for the memory blocks before the final - * sleep. -*/ - -void s3c_pm_check_store(void) -{ - if (crcs != NULL) - s3c_pm_run_sysram(s3c_pm_makecheck, crcs); -} - -/* in_region - * - * return TRUE if the area defined by ptr..ptr+size contains the - * what..what+whatsz -*/ - -static inline int in_region(void *ptr, int size, void *what, size_t whatsz) -{ - if ((what+whatsz) < ptr) - return 0; - - if (what > (ptr+size)) - return 0; - - return 1; -} - -/** - * s3c_pm_runcheck() - helper to check a resource on restore. - * @res: The resource to check - * @vak: Pointer to list of CRC32 values to check. - * - * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this - * function runs the given memory resource checking it against the stored - * CRC to ensure that memory is restored. The function tries to skip as - * many of the areas used during the suspend process. - */ -static u32 *s3c_pm_runcheck(struct resource *res, u32 *val) -{ - unsigned long addr; - unsigned long left; - void *stkpage; - void *ptr; - u32 calc; - - stkpage = (void *)((u32)&calc & ~PAGE_MASK); - - for (addr = res->start; addr < res->end; - addr += CHECK_CHUNKSIZE) { - left = res->end - addr; - - if (left > CHECK_CHUNKSIZE) - left = CHECK_CHUNKSIZE; - - ptr = phys_to_virt(addr); - - if (in_region(ptr, left, stkpage, 4096)) { - S3C_PMDBG("skipping %08lx, has stack in\n", addr); - goto skip_check; - } - - if (in_region(ptr, left, crcs, crc_size)) { - S3C_PMDBG("skipping %08lx, has crc block in\n", addr); - goto skip_check; - } - - /* calculate and check the checksum */ - - calc = crc32_le(~0, ptr, left); - if (calc != *val) { - printk(KERN_ERR "Restore CRC error at " - "%08lx (%08x vs %08x)\n", addr, calc, *val); - - S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", - addr, calc, *val); - } - - skip_check: - val++; - } - - return val; -} - -/** - * s3c_pm_check_restore() - memory check called on resume - * - * check the CRCs after the restore event and free the memory used - * to hold them -*/ -void s3c_pm_check_restore(void) -{ - if (crcs != NULL) - s3c_pm_run_sysram(s3c_pm_runcheck, crcs); -} - -/** - * s3c_pm_check_cleanup() - free memory resources - * - * Free the resources that where allocated by the suspend - * memory check code. We do this separately from the - * s3c_pm_check_restore() function as we cannot call any - * functions that might sleep during that resume. - */ -void s3c_pm_check_cleanup(void) -{ - kfree(crcs); - crcs = NULL; -} - diff --git a/arch/arm/plat-samsung/pm-debug.c b/arch/arm/plat-samsung/pm-debug.c deleted file mode 100644 index 482d53753e93..000000000000 --- a/arch/arm/plat-samsung/pm-debug.c +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Copyright (C) 2013 Samsung Electronics Co., Ltd. -// Tomasz Figa -// Copyright (C) 2008 Openmoko, Inc. -// Copyright (C) 2004-2008 Simtec Electronics -// Ben Dooks -// http://armlinux.simtec.co.uk/ -// -// Samsung common power management (suspend to RAM) debug support - -#include -#include -#include - -#include - -#include -#include - -static struct pm_uart_save uart_save; - -extern void printascii(const char *); - -void s3c_pm_dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsnprintf(buff, sizeof(buff), fmt, va); - va_end(va); - - printascii(buff); -} - -static inline void __iomem *s3c_pm_uart_base(void) -{ - unsigned long paddr; - unsigned long vaddr; - - debug_ll_addr(&paddr, &vaddr); - - return (void __iomem *)vaddr; -} - -void s3c_pm_save_uarts(bool is_s3c2410) -{ - void __iomem *regs = s3c_pm_uart_base(); - struct pm_uart_save *save = &uart_save; - - save->ulcon = __raw_readl(regs + S3C2410_ULCON); - save->ucon = __raw_readl(regs + S3C2410_UCON); - save->ufcon = __raw_readl(regs + S3C2410_UFCON); - save->umcon = __raw_readl(regs + S3C2410_UMCON); - save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV); - - if (!is_s3c2410) - save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT); - - S3C_PMDBG("UART[%p]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n", - regs, save->ulcon, save->ucon, save->ufcon, save->ubrdiv); -} - -void s3c_pm_restore_uarts(bool is_s3c2410) -{ - void __iomem *regs = s3c_pm_uart_base(); - struct pm_uart_save *save = &uart_save; - - s3c_pm_arch_update_uart(regs, save); - - __raw_writel(save->ulcon, regs + S3C2410_ULCON); - __raw_writel(save->ucon, regs + S3C2410_UCON); - __raw_writel(save->ufcon, regs + S3C2410_UFCON); - __raw_writel(save->umcon, regs + S3C2410_UMCON); - __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV); - - if (!is_s3c2410) - __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT); -} diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 264185664594..5abe82079d2e 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -35,7 +35,53 @@ config EXYNOS_PMU_ARM_DRIVERS config EXYNOS_PM_DOMAINS bool "Exynos PM domains" if COMPILE_TEST - depends on PM_GENERIC_DOMAINS || COMPILE_TEST + depends on (ARCH_EXYNOS && PM_GENERIC_DOMAINS) || COMPILE_TEST + +config SAMSUNG_PM_DEBUG + bool "Samsung PM Suspend debug" + depends on PM && DEBUG_KERNEL + depends on PLAT_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 + depends on DEBUG_S3C24XX_UART || DEBUG_S3C2410_UART + help + Say Y here if you want verbose debugging from the PM Suspend and + Resume code. See + for more information. + +config S3C_PM_DEBUG_LED_SMDK + bool "SMDK LED suspend/resume debugging" + depends on PM && (MACH_SMDK6410) + help + Say Y here to enable the use of the SMDK LEDs on the baseboard + for debugging of the state of the suspend and resume process. + + Note, this currently only works for S3C64XX based SMDK boards. + +config SAMSUNG_PM_CHECK + bool "S3C2410 PM Suspend Memory CRC" + depends on PM && (PLAT_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210) + select CRC32 + help + Enable the PM code's memory area checksum over sleep. This option + will generate CRCs of all blocks of memory, and store them before + going to sleep. The blocks are then checked on resume for any + errors. + + Note, this can take several seconds depending on memory size + and CPU speed. + + See + +config SAMSUNG_PM_CHECK_CHUNKSIZE + int "S3C2410 PM Suspend CRC Chunksize (KiB)" + depends on PM && SAMSUNG_PM_CHECK + default 64 + help + Set the chunksize in Kilobytes of the CRC for checking memory + corruption over suspend and resume. A smaller value will mean that + the CRC data block will take more memory, but will identify any + faults with better precision. + + See config EXYNOS_REGULATOR_COUPLER bool "Exynos SoC Regulator Coupler" if COMPILE_TEST diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index ecc3a32f6406..59e8e9453f27 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -10,3 +10,6 @@ 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 obj-$(CONFIG_EXYNOS_REGULATOR_COUPLER) += exynos-regulator-coupler.o + +obj-$(CONFIG_SAMSUNG_PM_CHECK) += s3c-pm-check.o +obj-$(CONFIG_SAMSUNG_PM_DEBUG) += s3c-pm-debug.o diff --git a/drivers/soc/samsung/s3c-pm-check.c b/drivers/soc/samsung/s3c-pm-check.c new file mode 100644 index 000000000000..ff3e099fc208 --- /dev/null +++ b/drivers/soc/samsung/s3c-pm-check.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// originally in linux/arch/arm/plat-s3c24xx/pm.c +// +// Copyright (c) 2004-2008 Simtec Electronics +// http://armlinux.simtec.co.uk +// Ben Dooks +// +// S3C Power Mangament - suspend/resume memory corruption check. + +#include +#include +#include +#include +#include +#include + +#include + +#if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1 +#error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value +#endif + +/* suspend checking code... + * + * this next area does a set of crc checks over all the installed + * memory, so the system can verify if the resume was ok. + * + * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, + * increasing it will mean that the area corrupted will be less easy to spot, + * and reducing the size will cause the CRC save area to grow +*/ + +#define CHECK_CHUNKSIZE (CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024) + +static u32 crc_size; /* size needed for the crc block */ +static u32 *crcs; /* allocated over suspend/resume */ + +typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); + +/* s3c_pm_run_res + * + * go through the given resource list, and look for system ram +*/ + +static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) +{ + while (ptr != NULL) { + if (ptr->child != NULL) + s3c_pm_run_res(ptr->child, fn, arg); + + if ((ptr->flags & IORESOURCE_SYSTEM_RAM) + == IORESOURCE_SYSTEM_RAM) { + S3C_PMDBG("Found system RAM at %08lx..%08lx\n", + (unsigned long)ptr->start, + (unsigned long)ptr->end); + arg = (fn)(ptr, arg); + } + + ptr = ptr->sibling; + } +} + +static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) +{ + s3c_pm_run_res(&iomem_resource, fn, arg); +} + +static u32 *s3c_pm_countram(struct resource *res, u32 *val) +{ + u32 size = (u32)resource_size(res); + + size += CHECK_CHUNKSIZE-1; + size /= CHECK_CHUNKSIZE; + + S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", + (unsigned long)res->start, (unsigned long)res->end, size); + + *val += size * sizeof(u32); + return val; +} + +/* s3c_pm_prepare_check + * + * prepare the necessary information for creating the CRCs. This + * must be done before the final save, as it will require memory + * allocating, and thus touching bits of the kernel we do not + * know about. +*/ + +void s3c_pm_check_prepare(void) +{ + crc_size = 0; + + s3c_pm_run_sysram(s3c_pm_countram, &crc_size); + + S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size); + + crcs = kmalloc(crc_size+4, GFP_KERNEL); + if (crcs == NULL) + printk(KERN_ERR "Cannot allocated CRC save area\n"); +} + +static u32 *s3c_pm_makecheck(struct resource *res, u32 *val) +{ + unsigned long addr, left; + + for (addr = res->start; addr < res->end; + addr += CHECK_CHUNKSIZE) { + left = res->end - addr; + + if (left > CHECK_CHUNKSIZE) + left = CHECK_CHUNKSIZE; + + *val = crc32_le(~0, phys_to_virt(addr), left); + val++; + } + + return val; +} + +/* s3c_pm_check_store + * + * compute the CRC values for the memory blocks before the final + * sleep. +*/ + +void s3c_pm_check_store(void) +{ + if (crcs != NULL) + s3c_pm_run_sysram(s3c_pm_makecheck, crcs); +} + +/* in_region + * + * return TRUE if the area defined by ptr..ptr+size contains the + * what..what+whatsz +*/ + +static inline int in_region(void *ptr, int size, void *what, size_t whatsz) +{ + if ((what+whatsz) < ptr) + return 0; + + if (what > (ptr+size)) + return 0; + + return 1; +} + +/** + * s3c_pm_runcheck() - helper to check a resource on restore. + * @res: The resource to check + * @vak: Pointer to list of CRC32 values to check. + * + * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this + * function runs the given memory resource checking it against the stored + * CRC to ensure that memory is restored. The function tries to skip as + * many of the areas used during the suspend process. + */ +static u32 *s3c_pm_runcheck(struct resource *res, u32 *val) +{ + unsigned long addr; + unsigned long left; + void *stkpage; + void *ptr; + u32 calc; + + stkpage = (void *)((u32)&calc & ~PAGE_MASK); + + for (addr = res->start; addr < res->end; + addr += CHECK_CHUNKSIZE) { + left = res->end - addr; + + if (left > CHECK_CHUNKSIZE) + left = CHECK_CHUNKSIZE; + + ptr = phys_to_virt(addr); + + if (in_region(ptr, left, stkpage, 4096)) { + S3C_PMDBG("skipping %08lx, has stack in\n", addr); + goto skip_check; + } + + if (in_region(ptr, left, crcs, crc_size)) { + S3C_PMDBG("skipping %08lx, has crc block in\n", addr); + goto skip_check; + } + + /* calculate and check the checksum */ + + calc = crc32_le(~0, ptr, left); + if (calc != *val) { + printk(KERN_ERR "Restore CRC error at " + "%08lx (%08x vs %08x)\n", addr, calc, *val); + + S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", + addr, calc, *val); + } + + skip_check: + val++; + } + + return val; +} + +/** + * s3c_pm_check_restore() - memory check called on resume + * + * check the CRCs after the restore event and free the memory used + * to hold them +*/ +void s3c_pm_check_restore(void) +{ + if (crcs != NULL) + s3c_pm_run_sysram(s3c_pm_runcheck, crcs); +} + +/** + * s3c_pm_check_cleanup() - free memory resources + * + * Free the resources that where allocated by the suspend + * memory check code. We do this separately from the + * s3c_pm_check_restore() function as we cannot call any + * functions that might sleep during that resume. + */ +void s3c_pm_check_cleanup(void) +{ + kfree(crcs); + crcs = NULL; +} + diff --git a/drivers/soc/samsung/s3c-pm-debug.c b/drivers/soc/samsung/s3c-pm-debug.c new file mode 100644 index 000000000000..b5ce0e9a41e5 --- /dev/null +++ b/drivers/soc/samsung/s3c-pm-debug.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2013 Samsung Electronics Co., Ltd. +// Tomasz Figa +// Copyright (C) 2008 Openmoko, Inc. +// Copyright (C) 2004-2008 Simtec Electronics +// Ben Dooks +// http://armlinux.simtec.co.uk/ +// +// Samsung common power management (suspend to RAM) debug support + +#include +#include +#include + +#include + +#include + +static struct pm_uart_save uart_save; + +extern void printascii(const char *); + +void s3c_pm_dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vsnprintf(buff, sizeof(buff), fmt, va); + va_end(va); + + printascii(buff); +} + +static inline void __iomem *s3c_pm_uart_base(void) +{ + unsigned long paddr; + unsigned long vaddr; + + debug_ll_addr(&paddr, &vaddr); + + return (void __iomem *)vaddr; +} + +void s3c_pm_save_uarts(bool is_s3c2410) +{ + void __iomem *regs = s3c_pm_uart_base(); + struct pm_uart_save *save = &uart_save; + + save->ulcon = __raw_readl(regs + S3C2410_ULCON); + save->ucon = __raw_readl(regs + S3C2410_UCON); + save->ufcon = __raw_readl(regs + S3C2410_UFCON); + save->umcon = __raw_readl(regs + S3C2410_UMCON); + save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV); + + if (!is_s3c2410) + save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT); + + S3C_PMDBG("UART[%p]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n", + regs, save->ulcon, save->ucon, save->ufcon, save->ubrdiv); +} + +void s3c_pm_restore_uarts(bool is_s3c2410) +{ + void __iomem *regs = s3c_pm_uart_base(); + struct pm_uart_save *save = &uart_save; + + s3c_pm_arch_update_uart(regs, save); + + __raw_writel(save->ulcon, regs + S3C2410_ULCON); + __raw_writel(save->ucon, regs + S3C2410_UCON); + __raw_writel(save->ufcon, regs + S3C2410_UFCON); + __raw_writel(save->umcon, regs + S3C2410_UMCON); + __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV); + + if (!is_s3c2410) + __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT); +} diff --git a/include/linux/soc/samsung/s3c-pm.h b/include/linux/soc/samsung/s3c-pm.h new file mode 100644 index 000000000000..730bd1d3d09a --- /dev/null +++ b/include/linux/soc/samsung/s3c-pm.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Tomasz Figa + * Copyright (c) 2004 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Written by Ben Dooks, + */ + +#ifndef __LINUX_SOC_SAMSUNG_S3C_PM_H +#define __LINUX_SOC_SAMSUNG_S3C_PM_H __FILE__ + +#include + +/* PM debug functions */ + +/** + * struct pm_uart_save - save block for core UART + * @ulcon: Save value for S3C2410_ULCON + * @ucon: Save value for S3C2410_UCON + * @ufcon: Save value for S3C2410_UFCON + * @umcon: Save value for S3C2410_UMCON + * @ubrdiv: Save value for S3C2410_UBRDIV + * + * Save block for UART registers to be held over sleep and restored if they + * are needed (say by debug). +*/ +struct pm_uart_save { + u32 ulcon; + u32 ucon; + u32 ufcon; + u32 umcon; + u32 ubrdiv; + u32 udivslot; +}; + +#ifdef CONFIG_SAMSUNG_PM_DEBUG +/** + * s3c_pm_dbg() - low level debug function for use in suspend/resume. + * @msg: The message to print. + * + * This function is used mainly to debug the resume process before the system + * can rely on printk/console output. It uses the low-level debugging output + * routine printascii() to do its work. + */ +extern void s3c_pm_dbg(const char *msg, ...); + +#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt) + +extern void s3c_pm_save_uarts(bool is_s3c24xx); +extern void s3c_pm_restore_uarts(bool is_s3c24xx); + +#ifdef CONFIG_ARCH_S3C64XX +extern void s3c_pm_arch_update_uart(void __iomem *regs, + struct pm_uart_save *save); +#else +static inline void +s3c_pm_arch_update_uart(void __iomem *regs, struct pm_uart_save *save) +{ +} +#endif + +#else +#define S3C_PMDBG(fmt...) pr_debug(fmt) + +static inline void s3c_pm_save_uarts(bool is_s3c24xx) { } +static inline void s3c_pm_restore_uarts(bool is_s3c24xx) { } +#endif + +/* suspend memory checking */ + +#ifdef CONFIG_SAMSUNG_PM_CHECK +extern void s3c_pm_check_prepare(void); +extern void s3c_pm_check_restore(void); +extern void s3c_pm_check_cleanup(void); +extern void s3c_pm_check_store(void); +#else +#define s3c_pm_check_prepare() do { } while (0) +#define s3c_pm_check_restore() do { } while (0) +#define s3c_pm_check_cleanup() do { } while (0) +#define s3c_pm_check_store() do { } while (0) +#endif + +#endif -- cgit v1.2.3 From 9f467393e2973f124661426953811b4758181088 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 15 Jul 2020 10:47:01 +0800 Subject: soc: integrator: Drop pointless static qualifier in integrator_soc_init() There is no need to have the 'struct regmap *syscon_regmap' variable static since new value always be assigned before use it. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20200715024701.28356-1-yuehaibing@huawei.com Signed-off-by: Linus Walleij --- drivers/soc/versatile/soc-integrator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/versatile/soc-integrator.c b/drivers/soc/versatile/soc-integrator.c index 7dcf77ccd31e..bab4ad87aa75 100644 --- a/drivers/soc/versatile/soc-integrator.c +++ b/drivers/soc/versatile/soc-integrator.c @@ -100,7 +100,7 @@ ATTRIBUTE_GROUPS(integrator); static int __init integrator_soc_init(void) { - static struct regmap *syscon_regmap; + struct regmap *syscon_regmap; struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; struct device_node *np; -- cgit v1.2.3 From 2152fbbd47c06c4f50ad265ec1b0c43673bee3e8 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 3 Jul 2020 09:07:29 -0700 Subject: soc: ti: pm33xx: Simplify RTC usage to prepare to drop platform data We must re-enable the RTC module clock enabled in RTC+DDR suspend, and pm33xx has been using platform data callbacks for that. Looks like for retention suspend the RTC module clock must not be re-enabled. To remove the legacy platform data callbacks, and eventually be able to drop the RTC legacy platform data, let's manage the RTC module clock and register range directly in pm33xx. Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/pm33xx-core.c | 25 ------------------- drivers/soc/ti/pm33xx.c | 47 ++++++++++++++++++++++++++++++++---- include/linux/platform_data/pm33xx.h | 3 --- 3 files changed, 42 insertions(+), 33 deletions(-) (limited to 'drivers/soc') diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c index 58236c7dc83e..87c347e97af8 100644 --- a/arch/arm/mach-omap2/pm33xx-core.c +++ b/arch/arm/mach-omap2/pm33xx-core.c @@ -25,7 +25,6 @@ #include "control.h" #include "clockdomain.h" #include "iomap.h" -#include "omap_hwmod.h" #include "pm.h" #include "powerdomain.h" #include "prm33xx.h" @@ -36,7 +35,6 @@ static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm; static struct clockdomain *gfx_l4ls_clkdm; static void __iomem *scu_base; -static struct omap_hwmod *rtc_oh; static int (*idle_fn)(u32 wfi_flags); @@ -267,13 +265,6 @@ static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void) return NULL; } -static void __iomem *am43xx_get_rtc_base_addr(void) -{ - rtc_oh = omap_hwmod_lookup("rtc"); - - return omap_hwmod_get_mpu_rt_va(rtc_oh); -} - static void am43xx_save_context(void) { } @@ -297,16 +288,6 @@ static void am43xx_restore_context(void) writel_relaxed(0x0, AM33XX_L4_WK_IO_ADDRESS(0x44df2e14)); } -static void am43xx_prepare_rtc_suspend(void) -{ - omap_hwmod_enable(rtc_oh); -} - -static void am43xx_prepare_rtc_resume(void) -{ - omap_hwmod_idle(rtc_oh); -} - static struct am33xx_pm_platform_data am33xx_ops = { .init = am33xx_suspend_init, .deinit = amx3_suspend_deinit, @@ -317,10 +298,7 @@ static struct am33xx_pm_platform_data am33xx_ops = { .get_sram_addrs = amx3_get_sram_addrs, .save_context = am33xx_save_context, .restore_context = am33xx_restore_context, - .prepare_rtc_suspend = am43xx_prepare_rtc_suspend, - .prepare_rtc_resume = am43xx_prepare_rtc_resume, .check_off_mode_enable = am33xx_check_off_mode_enable, - .get_rtc_base_addr = am43xx_get_rtc_base_addr, }; static struct am33xx_pm_platform_data am43xx_ops = { @@ -333,10 +311,7 @@ static struct am33xx_pm_platform_data am43xx_ops = { .get_sram_addrs = amx3_get_sram_addrs, .save_context = am43xx_save_context, .restore_context = am43xx_restore_context, - .prepare_rtc_suspend = am43xx_prepare_rtc_suspend, - .prepare_rtc_resume = am43xx_prepare_rtc_resume, .check_off_mode_enable = am43xx_check_off_mode_enable, - .get_rtc_base_addr = am43xx_get_rtc_base_addr, }; static struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void) diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index de0123ec8ad6..d2f5e7001a93 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ #define GIC_INT_SET_PENDING_BASE 0x200 #define AM43XX_GIC_DIST_BASE 0x48241000 +static void __iomem *rtc_base_virt; +static struct clk *rtc_fck; static u32 rtc_magic_val; static int (*am33xx_do_wfi_sram)(unsigned long unused); @@ -90,7 +93,7 @@ static int am33xx_push_sram_idle(void) ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; ro_sram_data.amx3_pm_sram_data_phys = gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); - ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr(); + ro_sram_data.rtc_base_virt = rtc_base_virt; /* Save physical address to calculate resume offset during pm init */ am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, @@ -158,7 +161,7 @@ static struct wkup_m3_wakeup_src rtc_wake_src(void) { u32 i; - i = __raw_readl(pm_ops->get_rtc_base_addr() + 0x44) & 0x40; + i = __raw_readl(rtc_base_virt + 0x44) & 0x40; if (i) { retrigger_irq = rtc_alarm_wakeup.irq_nr; @@ -177,13 +180,24 @@ static int am33xx_rtc_only_idle(unsigned long wfi_flags) return 0; } +/* + * Note that the RTC module clock must be re-enabled only for rtc+ddr suspend. + * And looks like the module can stay in SYSC_IDLE_SMART_WKUP mode configured + * by the interconnect code just fine for both rtc+ddr suspend and retention + * suspend. + */ static int am33xx_pm_suspend(suspend_state_t suspend_state) { int i, ret = 0; if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) { - pm_ops->prepare_rtc_suspend(); + ret = clk_prepare_enable(rtc_fck); + if (ret) { + dev_err(pm33xx_dev, "Failed to enable clock: %i\n", ret); + return ret; + } + pm_ops->save_context(); suspend_wfi_flags |= WFI_FLAG_RTC_ONLY; clk_save_context(); @@ -236,7 +250,7 @@ static int am33xx_pm_suspend(suspend_state_t suspend_state) } if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) - pm_ops->prepare_rtc_resume(); + clk_disable_unprepare(rtc_fck); return ret; } @@ -425,14 +439,28 @@ static int am33xx_pm_rtc_setup(void) struct device_node *np; unsigned long val = 0; struct nvmem_device *nvmem; + int error; np = of_find_node_by_name(NULL, "rtc"); if (of_device_is_available(np)) { + /* RTC interconnect target module clock */ + rtc_fck = of_clk_get_by_name(np->parent, "fck"); + if (IS_ERR(rtc_fck)) + return PTR_ERR(rtc_fck); + + rtc_base_virt = of_iomap(np, 0); + if (!rtc_base_virt) { + pr_warn("PM: could not iomap rtc"); + error = -ENODEV; + goto err_clk_put; + } + omap_rtc = rtc_class_open("rtc0"); if (!omap_rtc) { pr_warn("PM: rtc0 not available"); - return -EPROBE_DEFER; + error = -EPROBE_DEFER; + goto err_iounmap; } nvmem = devm_nvmem_device_get(&omap_rtc->dev, @@ -454,6 +482,13 @@ static int am33xx_pm_rtc_setup(void) } return 0; + +err_iounmap: + iounmap(rtc_base_virt); +err_clk_put: + clk_put(rtc_fck); + + return error; } static int am33xx_pm_probe(struct platform_device *pdev) @@ -544,6 +579,8 @@ static int am33xx_pm_remove(struct platform_device *pdev) suspend_set_ops(NULL); wkup_m3_ipc_put(m3_ipc); am33xx_pm_free_sram(); + iounmap(rtc_base_virt); + clk_put(rtc_fck); return 0; } diff --git a/include/linux/platform_data/pm33xx.h b/include/linux/platform_data/pm33xx.h index 644af1d89cfa..7037ba7a53ca 100644 --- a/include/linux/platform_data/pm33xx.h +++ b/include/linux/platform_data/pm33xx.h @@ -54,11 +54,8 @@ struct am33xx_pm_platform_data { void (*begin_suspend)(void); void (*finish_suspend)(void); struct am33xx_pm_sram_addr *(*get_sram_addrs)(void); - void __iomem *(*get_rtc_base_addr)(void); void (*save_context)(void); void (*restore_context)(void); - void (*prepare_rtc_suspend)(void); - void (*prepare_rtc_resume)(void); int (*check_off_mode_enable)(void); }; -- cgit v1.2.3