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@free-electrons.com>
Acked-by: Wenyou Yang <wenyou.yang@atmel.com>
This commit is contained in:
Alexandre Belloni 2016-09-27 12:29:50 +02:00
Родитель 2ea659a9ef
Коммит 24a0f5c539
6 изменённых файлов: 187 добавлений и 23 удалений

Просмотреть файл

@ -15,10 +15,12 @@
extern void __init at91rm9200_pm_init(void); extern void __init at91rm9200_pm_init(void);
extern void __init at91sam9_pm_init(void); extern void __init at91sam9_pm_init(void);
extern void __init sama5_pm_init(void); extern void __init sama5_pm_init(void);
extern void __init sama5d2_pm_init(void);
#else #else
static inline void __init at91rm9200_pm_init(void) { } static inline void __init at91rm9200_pm_init(void) { }
static inline void __init at91sam9_pm_init(void) { } static inline void __init at91sam9_pm_init(void) { }
static inline void __init sama5_pm_init(void) { } static inline void __init sama5_pm_init(void) { }
static inline void __init sama5d2_pm_init(void) { }
#endif #endif
#endif /* _AT91_GENERIC_H */ #endif /* _AT91_GENERIC_H */

Просмотреть файл

@ -22,6 +22,7 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/fncpy.h> #include <asm/fncpy.h>
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <asm/suspend.h>
#include "generic.h" #include "generic.h"
#include "pm.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; 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 void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data);
extern u32 at91_pm_suspend_in_sram_sz; 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(); flush_cache_all();
outer_disable(); outer_disable();
at91_suspend_sram_fn(&pm_data); 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(); outer_resume();
} }
@ -436,6 +469,70 @@ static void __init at91_pm_sram_init(void)
&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz); &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
} }
static void __init at91_pm_backup_init(void)
{
struct gen_pool *sram_pool;
struct device_node *np;
struct platform_device *pdev = NULL;
pm_bu = NULL;
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
if (!np) {
pr_warn("%s: failed to find shdwc!\n", __func__);
return;
}
pm_data.shdwc = of_iomap(np, 0);
of_node_put(np);
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
if (!np) {
pr_warn("%s: failed to find sfrbu!\n", __func__);
goto sfrbu_fail;
}
pm_data.sfrbu = of_iomap(np, 0);
of_node_put(np);
pm_bu = NULL;
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
if (!np)
goto securam_fail;
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev) {
pr_warn("%s: failed to find securam device!\n", __func__);
goto securam_fail;
}
sram_pool = gen_pool_get(&pdev->dev, NULL);
if (!sram_pool) {
pr_warn("%s: securam pool unavailable!\n", __func__);
goto securam_fail;
}
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__);
goto securam_fail;
}
pm_bu->suspended = 0;
pm_bu->canary = virt_to_phys(&canary);
pm_bu->resume = virt_to_phys(cpu_resume);
return;
sfrbu_fail:
iounmap(pm_data.shdwc);
pm_data.shdwc = NULL;
securam_fail:
iounmap(pm_data.sfrbu);
pm_data.sfrbu = NULL;
}
struct pmc_info { struct pmc_info {
unsigned long uhp_udp_mask; unsigned long uhp_udp_mask;
}; };
@ -510,3 +607,9 @@ void __init sama5_pm_init(void)
at91_dt_ramc(); at91_dt_ramc();
at91_pm_init(NULL); at91_pm_init(NULL);
} }
void __init sama5d2_pm_init(void)
{
at91_pm_backup_init();
sama5_pm_init();
}

Просмотреть файл

@ -22,6 +22,7 @@
#define AT91_MEMCTRL_DDRSDR 2 #define AT91_MEMCTRL_DDRSDR 2
#define AT91_PM_SLOW_CLOCK 0x01 #define AT91_PM_SLOW_CLOCK 0x01
#define AT91_PM_BACKUP 0x02
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
struct at91_pm_data { struct at91_pm_data {
@ -30,6 +31,9 @@ struct at91_pm_data {
unsigned long uhp_udp_mask; unsigned long uhp_udp_mask;
unsigned int memctrl; unsigned int memctrl;
unsigned int mode; unsigned int mode;
void __iomem *shdwc;
void __iomem *sfrbu;
unsigned int deepest_state;
}; };
#endif #endif

Просмотреть файл

@ -9,5 +9,8 @@ int main(void)
DEFINE(PM_DATA_RAMC1, offsetof(struct at91_pm_data, ramc[1])); DEFINE(PM_DATA_RAMC1, offsetof(struct at91_pm_data, ramc[1]));
DEFINE(PM_DATA_MEMCTRL, offsetof(struct at91_pm_data, memctrl)); DEFINE(PM_DATA_MEMCTRL, offsetof(struct at91_pm_data, memctrl));
DEFINE(PM_DATA_MODE, offsetof(struct at91_pm_data, mode)); 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; return 0;
} }

Просмотреть файл

@ -97,15 +97,61 @@ ENTRY(at91_pm_suspend_in_sram)
str tmp1, .memtype str tmp1, .memtype
ldr tmp1, [r0, #PM_DATA_MODE] ldr tmp1, [r0, #PM_DATA_MODE]
str tmp1, .pm_mode str tmp1, .pm_mode
/* Both ldrne below are here to preload their address in the TLB */
ldr tmp1, [r0, #PM_DATA_SHDWC]
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]
/* Active the self-refresh mode */ /* Active the self-refresh mode */
mov r0, #SRAMC_SELF_FRESH_ACTIVE mov r0, #SRAMC_SELF_FRESH_ACTIVE
bl at91_sramc_self_refresh bl at91_sramc_self_refresh
ldr r0, .pm_mode ldr r0, .pm_mode
tst r0, #AT91_PM_SLOW_CLOCK cmp r0, #AT91_PM_SLOW_CLOCK
beq skip_disable_main_clock beq slow_clock
cmp r0, #AT91_PM_BACKUP
beq backup_mode
/* Wait for interrupt */
ldr pmc, .pmc_base
at91_cpu_idle
b exit_suspend
slow_clock:
bl at91_slowck_mode
b exit_suspend
backup_mode:
bl at91_backup_mode
b exit_suspend
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)
ENTRY(at91_backup_mode)
/*BUMEN*/
ldr r0, .sfr
mov tmp1, #0x1
str tmp1, [r0, #0x10]
/* Shutdown */
ldr r0, .shdwc
mov tmp1, #0xA5000000
add tmp1, tmp1, #0x1
str tmp1, [r0, #0]
ENDPROC(at91_backup_mode)
ENTRY(at91_slowck_mode)
ldr pmc, .pmc_base ldr pmc, .pmc_base
/* Save Master clock setting */ /* Save Master clock setting */
@ -134,18 +180,9 @@ ENTRY(at91_pm_suspend_in_sram)
orr tmp1, tmp1, #AT91_PMC_KEY orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR] str tmp1, [pmc, #AT91_CKGR_MOR]
skip_disable_main_clock:
ldr pmc, .pmc_base
/* Wait for interrupt */ /* Wait for interrupt */
at91_cpu_idle 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 */ /* Turn on the main oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR] ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCEN orr tmp1, tmp1, #AT91_PMC_MOSCEN
@ -174,14 +211,8 @@ skip_disable_main_clock:
wait_mckrdy wait_mckrdy
skip_enable_main_clock: mov pc, lr
/* Exit the self-refresh mode */ ENDPROC(at91_slowck_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)
/* /*
* void at91_sramc_self_refresh(unsigned int is_active) * void at91_sramc_self_refresh(unsigned int is_active)
@ -314,6 +345,10 @@ ENDPROC(at91_sramc_self_refresh)
.word 0 .word 0
.sramc1_base: .sramc1_base:
.word 0 .word 0
.shdwc:
.word 0
.sfr:
.word 0
.memtype: .memtype:
.word 0 .word 0
.pm_mode: .pm_mode:

Просмотреть файл

@ -34,7 +34,6 @@ DT_MACHINE_START(sama5_dt, "Atmel SAMA5")
MACHINE_END MACHINE_END
static const char *const sama5_alt_dt_board_compat[] __initconst = { static const char *const sama5_alt_dt_board_compat[] __initconst = {
"atmel,sama5d2",
"atmel,sama5d4", "atmel,sama5d4",
NULL NULL
}; };
@ -45,3 +44,21 @@ DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
.dt_compat = sama5_alt_dt_board_compat, .dt_compat = sama5_alt_dt_board_compat,
.l2c_aux_mask = ~0UL, .l2c_aux_mask = ~0UL,
MACHINE_END 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