Skip to content

Commit

Permalink
ARM: at91: pm: Add sama5d2 backup mode
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alexandre Belloni committed May 15, 2017
1 parent 2ea659a commit 24a0f5c
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 23 deletions.
2 changes: 2 additions & 0 deletions arch/arm/mach-at91/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
109 changes: 106 additions & 3 deletions arch/arm/mach-at91/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -436,6 +469,70 @@ static void __init at91_pm_sram_init(void)
&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 {
unsigned long uhp_udp_mask;
};
Expand Down Expand Up @@ -510,3 +607,9 @@ void __init sama5_pm_init(void)
at91_dt_ramc();
at91_pm_init(NULL);
}

void __init sama5d2_pm_init(void)
{
at91_pm_backup_init();
sama5_pm_init();
}
4 changes: 4 additions & 0 deletions arch/arm/mach-at91/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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

Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mach-at91/pm_data-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
73 changes: 54 additions & 19 deletions arch/arm/mach-at91/pm_suspend.S
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,61 @@ ENTRY(at91_pm_suspend_in_sram)
str tmp1, .memtype
ldr tmp1, [r0, #PM_DATA_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 */
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
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

/* Save Master clock setting */
Expand Down Expand Up @@ -134,18 +180,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
Expand Down Expand Up @@ -174,14 +211,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)
Expand Down Expand Up @@ -314,6 +345,10 @@ ENDPROC(at91_sramc_self_refresh)
.word 0
.sramc1_base:
.word 0
.shdwc:
.word 0
.sfr:
.word 0
.memtype:
.word 0
.pm_mode:
Expand Down
19 changes: 18 additions & 1 deletion arch/arm/mach-at91/sama5.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand All @@ -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

0 comments on commit 24a0f5c

Please sign in to comment.