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:
Родитель
2ea659a9ef
Коммит
24a0f5c539
|
@ -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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче