[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20170426160419.22401-1-alexandre.belloni@free-electrons.com>
Date: Wed, 26 Apr 2017 18:04:17 +0200
From: Alexandre Belloni <alexandre.belloni@...e-electrons.com>
To: Nicolas Ferre <nicolas.ferre@...rochip.com>
Cc: linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
Wenyou.Yang@...rochip.com,
Alexandre Belloni <alexandre.belloni@...e-electrons.com>
Subject: [PATCH 1/3] ARM: at91: pm: Add sama5d2 backup mode
The sama5d2 has a mode were it is possible to cut power to the SoC while
keeping the RAM in self refresh.
Resuming from that mode needs support in the firmware/bootloader.
Signed-off-by: Alexandre Belloni <alexandre.belloni@...e-electrons.com>
---
arch/arm/mach-at91/Makefile | 4 ++
arch/arm/mach-at91/generic.h | 2 +
arch/arm/mach-at91/pm.c | 103 ++++++++++++++++++++++++++++++++++-
arch/arm/mach-at91/pm.h | 4 ++
arch/arm/mach-at91/pm_data-offsets.c | 3 +
arch/arm/mach-at91/pm_suspend.S | 86 ++++++++++++++++++++++-------
arch/arm/mach-at91/sama5.c | 19 ++++++-
7 files changed, 198 insertions(+), 23 deletions(-)
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index cfd8f60a9268..87fe17dbdb56 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -14,6 +14,10 @@ obj-$(CONFIG_PM) += pm_suspend.o
ifeq ($(CONFIG_CPU_V7),y)
AFLAGS_pm_suspend.o := -march=armv7-a
endif
+# Backup mode will not compile for ARMv5 because of movt
+ifeq ($(CONFIG_SOC_SAMA5D2),y)
+AFLAGS_pm_suspend.o += -DBACKUP_MODE
+endif
ifeq ($(CONFIG_PM_DEBUG),y)
CFLAGS_pm.o += -DDEBUG
endif
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
index f1ead0f13c19..e2bd17237964 100644
--- a/arch/arm/mach-at91/generic.h
+++ b/arch/arm/mach-at91/generic.h
@@ -15,10 +15,12 @@
extern void __init at91rm9200_pm_init(void);
extern void __init at91sam9_pm_init(void);
extern void __init sama5_pm_init(void);
+extern void __init sama5d2_pm_init(void);
#else
static inline void __init at91rm9200_pm_init(void) { }
static inline void __init at91sam9_pm_init(void) { }
static inline void __init sama5_pm_init(void) { }
+static inline void __init sama5d2_pm_init(void) { }
#endif
#endif /* _AT91_GENERIC_H */
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 2cd27c830ab6..1e03f1277f14 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -22,6 +22,7 @@
#include <asm/cacheflush.h>
#include <asm/fncpy.h>
#include <asm/system_misc.h>
+#include <asm/suspend.h>
#include "generic.h"
#include "pm.h"
@@ -58,6 +59,14 @@ static int at91_pm_valid_state(suspend_state_t state)
}
}
+static int canary = 0xA5A5A5A5;
+
+static struct at91_pm_bu {
+ int suspended;
+ unsigned long reserved;
+ phys_addr_t canary;
+ phys_addr_t resume;
+} *pm_bu;
static suspend_state_t target_state;
@@ -123,15 +132,39 @@ static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
extern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data);
extern u32 at91_pm_suspend_in_sram_sz;
-static void at91_pm_suspend(suspend_state_t state)
+static int at91_suspend_finish(unsigned long val)
{
- pm_data.mode = (state == PM_SUSPEND_MEM) ? AT91_PM_SLOW_CLOCK : 0;
-
flush_cache_all();
outer_disable();
at91_suspend_sram_fn(&pm_data);
+ return 0;
+}
+
+static void at91_pm_suspend(suspend_state_t state)
+{
+ if (pm_data.deepest_state == AT91_PM_BACKUP)
+ if (state == PM_SUSPEND_MEM)
+ pm_data.mode = AT91_PM_BACKUP;
+ else
+ pm_data.mode = AT91_PM_SLOW_CLOCK;
+ else
+ pm_data.mode = (state == PM_SUSPEND_MEM) ? AT91_PM_SLOW_CLOCK : 0;
+
+ if (pm_data.mode == AT91_PM_BACKUP) {
+ pm_bu->suspended = 1;
+
+ cpu_suspend(0, at91_suspend_finish);
+
+ /* The SRAM is lost between suspend cycles */
+ at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
+ &at91_pm_suspend_in_sram,
+ at91_pm_suspend_in_sram_sz);
+ } else {
+ at91_suspend_finish(0);
+ }
+
outer_resume();
}
@@ -375,6 +408,25 @@ static __init void at91_dt_ramc(void)
at91_cpuidle_device.dev.platform_data = standby;
}
+static __init void at91_dt_shdwc(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
+ if (!np)
+ return;
+
+ pm_data.shdwc = of_iomap(np, 0);
+ of_node_put(np);
+
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
+ if (!np)
+ return;
+
+ pm_data.sfrbu = of_iomap(np, 0);
+ of_node_put(np);
+}
+
static void at91rm9200_idle(void)
{
/*
@@ -436,6 +488,44 @@ static void __init at91_pm_sram_init(void)
&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
}
+static void __init at91_pm_bu_sram_init(void)
+{
+ struct gen_pool *sram_pool;
+ struct device_node *node;
+ struct platform_device *pdev = NULL;
+
+ pm_bu = NULL;
+
+ for_each_compatible_node(node, NULL, "atmel,sama5d2-securam") {
+ pdev = of_find_device_by_node(node);
+ if (pdev) {
+ of_node_put(node);
+ break;
+ }
+ }
+
+ if (!pdev) {
+ pr_warn("%s: failed to find securam device!\n", __func__);
+ return;
+ }
+
+ sram_pool = gen_pool_get(&pdev->dev, NULL);
+ if (!sram_pool) {
+ pr_warn("%s: securam pool unavailable!\n", __func__);
+ return;
+ }
+
+ pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
+ if (!pm_bu) {
+ pr_warn("%s: unable to alloc securam!\n", __func__);
+ return;
+ }
+
+ pm_bu->suspended = 0;
+ pm_bu->canary = virt_to_phys(&canary);
+ pm_bu->resume = virt_to_phys(cpu_resume);
+}
+
struct pmc_info {
unsigned long uhp_udp_mask;
};
@@ -510,3 +600,10 @@ void __init sama5_pm_init(void)
at91_dt_ramc();
at91_pm_init(NULL);
}
+
+void __init sama5d2_pm_init(void)
+{
+ at91_dt_shdwc();
+ at91_pm_bu_sram_init();
+ sama5_pm_init();
+}
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
index fc0f7d048187..d9c6612ef62f 100644
--- a/arch/arm/mach-at91/pm.h
+++ b/arch/arm/mach-at91/pm.h
@@ -22,6 +22,7 @@
#define AT91_MEMCTRL_DDRSDR 2
#define AT91_PM_SLOW_CLOCK 0x01
+#define AT91_PM_BACKUP 0x02
#ifndef __ASSEMBLY__
struct at91_pm_data {
@@ -30,6 +31,9 @@ struct at91_pm_data {
unsigned long uhp_udp_mask;
unsigned int memctrl;
unsigned int mode;
+ void __iomem *shdwc;
+ void __iomem *sfrbu;
+ unsigned int deepest_state;
};
#endif
diff --git a/arch/arm/mach-at91/pm_data-offsets.c b/arch/arm/mach-at91/pm_data-offsets.c
index 30302cb16df0..c0a73e62b725 100644
--- a/arch/arm/mach-at91/pm_data-offsets.c
+++ b/arch/arm/mach-at91/pm_data-offsets.c
@@ -9,5 +9,8 @@ int main(void)
DEFINE(PM_DATA_RAMC1, offsetof(struct at91_pm_data, ramc[1]));
DEFINE(PM_DATA_MEMCTRL, offsetof(struct at91_pm_data, memctrl));
DEFINE(PM_DATA_MODE, offsetof(struct at91_pm_data, mode));
+ DEFINE(PM_DATA_SHDWC, offsetof(struct at91_pm_data, shdwc));
+ DEFINE(PM_DATA_SFRBU, offsetof(struct at91_pm_data, sfrbu));
+
return 0;
}
diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S
index 96781daa671a..b5ffa8e1f203 100644
--- a/arch/arm/mach-at91/pm_suspend.S
+++ b/arch/arm/mach-at91/pm_suspend.S
@@ -97,15 +97,74 @@ ENTRY(at91_pm_suspend_in_sram)
str tmp1, .memtype
ldr tmp1, [r0, #PM_DATA_MODE]
str tmp1, .pm_mode
+ ldr tmp1, [r0, #PM_DATA_SHDWC]
+#if defined(BACKUP_MODE)
+ str tmp1, .shdwc
+ cmp tmp1, #0
+ ldrne tmp2, [tmp1, #0]
+ ldr tmp1, [r0, #PM_DATA_SFRBU]
+ str tmp1, .sfr
+ cmp tmp1, #0
+ ldrne tmp2, [tmp1, #0x10]
+#endif
/* Active the self-refresh mode */
mov r0, #SRAMC_SELF_FRESH_ACTIVE
bl at91_sramc_self_refresh
ldr r0, .pm_mode
- tst r0, #AT91_PM_SLOW_CLOCK
- beq skip_disable_main_clock
+ cmp r0, #AT91_PM_SLOW_CLOCK
+ beq slow_clock
+#if defined(BACKUP_MODE)
+ cmp r0, #AT91_PM_BACKUP
+ beq backup_mode
+#endif
+ /* Wait for interrupt */
+ ldr pmc, .pmc_base
+ at91_cpu_idle
+ b exit_suspend
+
+slow_clock:
+ bl at91_slowck_mode
+ b exit_suspend
+#if defined(BACKUP_MODE)
+backup_mode:
+ bl at91_backup_mode
+ b exit_suspend
+#endif
+
+exit_suspend:
+ /* Exit the self-refresh mode */
+ mov r0, #SRAMC_SELF_FRESH_EXIT
+ bl at91_sramc_self_refresh
+
+ /* Restore registers, and return */
+ ldmfd sp!, {r4 - r12, pc}
+ENDPROC(at91_pm_suspend_in_sram)
+
+#if defined(BACKUP_MODE)
+ENTRY(at91_backup_mode)
+ #if 0
+ /* Read LPR */
+ ldr r2, .sramc_base
+ ldr r3, [r2, #AT91_DDRSDRC_LPR]
+ #endif
+
+ /*BUMEN*/
+ ldr r0, .sfr
+ mov tmp1, #(0x1)
+ str tmp1, [r0, #0x10]
+
+ /* Shutdown */
+ ldr r0, .shdwc
+ movw tmp1, #0x1
+ movt tmp1, #0xA500
+ str tmp1, [r0, #0]
+ENDPROC(at91_backup_mode)
+#endif
+
+ENTRY(at91_slowck_mode)
ldr pmc, .pmc_base
/* Save Master clock setting */
@@ -134,18 +193,9 @@ ENTRY(at91_pm_suspend_in_sram)
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
-skip_disable_main_clock:
- ldr pmc, .pmc_base
-
/* Wait for interrupt */
at91_cpu_idle
- ldr r0, .pm_mode
- tst r0, #AT91_PM_SLOW_CLOCK
- beq skip_enable_main_clock
-
- ldr pmc, .pmc_base
-
/* Turn on the main oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCEN
@@ -174,14 +224,8 @@ skip_disable_main_clock:
wait_mckrdy
-skip_enable_main_clock:
- /* Exit the self-refresh mode */
- mov r0, #SRAMC_SELF_FRESH_EXIT
- bl at91_sramc_self_refresh
-
- /* Restore registers, and return */
- ldmfd sp!, {r4 - r12, pc}
-ENDPROC(at91_pm_suspend_in_sram)
+ mov pc, lr
+ENDPROC(at91_slowck_mode)
/*
* void at91_sramc_self_refresh(unsigned int is_active)
@@ -314,6 +358,10 @@ ENDPROC(at91_sramc_self_refresh)
.word 0
.sramc1_base:
.word 0
+.shdwc:
+ .word 0
+.sfr:
+ .word 0
.memtype:
.word 0
.pm_mode:
diff --git a/arch/arm/mach-at91/sama5.c b/arch/arm/mach-at91/sama5.c
index 6d157d0ead8e..3d0bf95a56ae 100644
--- a/arch/arm/mach-at91/sama5.c
+++ b/arch/arm/mach-at91/sama5.c
@@ -34,7 +34,6 @@ DT_MACHINE_START(sama5_dt, "Atmel SAMA5")
MACHINE_END
static const char *const sama5_alt_dt_board_compat[] __initconst = {
- "atmel,sama5d2",
"atmel,sama5d4",
NULL
};
@@ -45,3 +44,21 @@ DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
.dt_compat = sama5_alt_dt_board_compat,
.l2c_aux_mask = ~0UL,
MACHINE_END
+
+static void __init sama5d2_init(void)
+{
+ of_platform_default_populate(NULL, NULL, NULL);
+ sama5d2_pm_init();
+}
+
+static const char *const sama5d2_compat[] __initconst = {
+ "atmel,sama5d2",
+ NULL
+};
+
+DT_MACHINE_START(sama5d2, "Atmel SAMA5")
+ /* Maintainer: Atmel */
+ .init_machine = sama5d2_init,
+ .dt_compat = sama5d2_compat,
+ .l2c_aux_mask = ~0UL,
+MACHINE_END
--
2.11.0
Powered by blists - more mailing lists