diff mbox

[1/3] ARM: at91: pm: Add sama5d2 backup mode

Message ID 20170426160419.22401-1-alexandre.belloni@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alexandre Belloni April 26, 2017, 4:04 p.m. UTC
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>
---
 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(-)

Comments

Romain Izard April 27, 2017, 1:34 p.m. UTC | #1
Hello Alexandre,

This series might also be of interest for the linux-pm mailing list.

2017-04-26 18:04 GMT+02:00 Alexandre Belloni
<alexandre.belloni@free-electrons.com>:
> 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>
> ---
>  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

We can rewrite the assembly to avoid using movt, and remove some ifdefs
from the code.


> 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;
> +               }
> +       }
> +

Do we really need to iterate over compatible nodes ?

> +       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);
> +}
> +

at91_pm_bu_sram_init and at91_dt_shdwc are necessary to use backup mode.
But those functions do not return error codes, and do no cleanup in case
of error.  I believe that it would be simpler if we only had a single
function.

>  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

If I understand this well, we are doing this to fill the TLB in advance
before the external RAM is put in self-refresh. It might be worthy of a
comment. Moreover, .pm_mode and .memtype do not need to be protected as
they are accessed during the at91_sramc_self_refresh, but .pmc_base
may need to be loaded in the TLB as well.

>
>         /* 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
> +

Do we need to keep this commented code ?

> +       /*BUMEN*/
> +       ldr     r0, .sfr
> +       mov     tmp1, #(0x1)

We don't need any parenthesis here

> +       str     tmp1, [r0, #0x10]
> +
> +       /* Shutdown */
> +       ldr     r0, .shdwc
> +       movw    tmp1, #0x1
> +       movt    tmp1, #0xA500

I believe the following assembly should do the same thing
without using v6+ instructions.

    mov    tmp1, #0xA5000000
    add    tmp1, tmp1, #0x1

> +       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

Best regards,
Alexandre Belloni April 27, 2017, 2:41 p.m. UTC | #2
On 27/04/2017 at 15:34:07 +0200, Romain Izard wrote:
> Hello Alexandre,
> 
> This series might also be of interest for the linux-pm mailing list.
> 

I don't think they care enough to review that.

> 2017-04-26 18:04 GMT+02:00 Alexandre Belloni
> > 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
> 
> We can rewrite the assembly to avoid using movt, and remove some ifdefs
> from the code.
> 

I'm kind of balanced there because I'm wondering whether we should
better separate what is in that assembly file because there are part of
it that have no chance to run on some platforms anyway.

But your solution is correct, I'll remove that.

> > +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;
> > +               }
> > +       }
> > +
> 
> Do we really need to iterate over compatible nodes ?
> 

You're right, this can probably be avoided.

> > +       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);
> > +}
> > +
> 
> at91_pm_bu_sram_init and at91_dt_shdwc are necessary to use backup mode.
> But those functions do not return error codes, and do no cleanup in case
> of error.  I believe that it would be simpler if we only had a single
> function.
> 

Yeah, this is kind of solved by adding the fallback in a latter patch
but I agree it can be done better.

> > 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
> 
> If I understand this well, we are doing this to fill the TLB in advance
> before the external RAM is put in self-refresh. It might be worthy of a
> comment. Moreover, .pm_mode and .memtype do not need to be protected as
> they are accessed during the at91_sramc_self_refresh, but .pmc_base
> may need to be loaded in the TLB as well.

We never had issue with .pmc_base because it is used in the C part of
the code, right before calling the assembly.
I'll add a comment.

> 
> > +#if defined(BACKUP_MODE)
> > +ENTRY(at91_backup_mode)
> > +       #if 0
> > +       /* Read LPR */
> > +       ldr     r2, .sramc_base
> > +       ldr     r3, [r2, #AT91_DDRSDRC_LPR]
> > +       #endif
> > +
> 
> Do we need to keep this commented code ?
> 

Nope, leftover from development
Wenyou.Yang@microchip.com April 28, 2017, 1:25 a.m. UTC | #3
On 2017/4/27 0:04, Alexandre Belloni wrote:
> 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>
> ---
>   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
diff mbox

Patch

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