Hi, Stefan

Anson Huang
Best Regards!


> -----Original Message-----
> From: Stefan Agner [mailto:ste...@agner.ch]
> Sent: Tuesday, August 7, 2018 7:59 PM
> To: Anson Huang <anson.hu...@nxp.com>
> Cc: sba...@denx.de; Fabio Estevam <fabio.este...@nxp.com>;
> albert.u.b...@aribaud.net; bryan.odonog...@linaro.org; Stefan Agner
> <stefan.ag...@toradex.com>; Peng Fan <peng....@nxp.com>;
> patrick.delau...@st.com; s...@chromium.org; Ye Li <ye...@nxp.com>;
> u-boot@lists.denx.de; dl-linux-imx <linux-...@nxp.com>
> Subject: Re: [U-Boot] [RESEND 3/3] imx: mx7: add system suspend/resume
> support
> 
> On 07.08.2018 08:46, Anson Huang wrote:
> > This patch adds system suspend/resume support, when linux kernel
> > enters deep sleep mode, SoC will go into below mode:
> >
> >  - CA7 platform goes into STOP mode;
> >  - SoC goes into DSM mode;
> >  - DDR goes into self-refresh mode;
> >  - CPU0/SCU will be powered down.
> >
> > When wake up event arrives:
> >
> >  - SoC DSM mdoe exits;
> >  - CA7 platform exit STOP mode, SCU/CPU0 power up;
> >  - Invalidate L1 cache;
> >  - DDR exit self-refresh mode;
> >  - Do secure monitor mode related initialization;
> >  - Jump to linux kernel resume entry.
> >
> > Belwo is the log of 1 iteration of system suspend/resume:
> >
> > [  338.824862] PM: suspend entry (deep) [  338.828853] PM: Syncing
> > filesystems ... done.
> > [  338.834433] Freezing user space processes ... (elapsed 0.001 seconds)
> done.
> > [  338.842939] OOM killer disabled.
> > [  338.846182] Freezing remaining freezable tasks ... (elapsed 0.001
> > seconds) done.
> > [  338.869717] PM: suspend devices took 0.010 seconds [  338.877846]
> > Disabling non-boot CPUs ...
> > [  338.960301] Retrying again to check for CPU kill [  338.964953]
> > CPU1 killed.
> > [  338.968104] Enabling non-boot CPUs ...
> > [  338.973598] CPU1 is up
> > [  339.267155] mmc1: queuing unknown CIS tuple 0x80 (2 bytes) [
> > 339.275833] mmc1: queuing unknown CIS tuple 0x80 (7 bytes) [
> > 339.284158] mmc1: queuing unknown CIS tuple 0x80 (6 bytes) [
> > 339.385065] PM: resume devices took 0.400 seconds [  339.389836] OOM
> > killer enabled.
> > [  339.392986] Restarting tasks ... done.
> > [  339.398990] PM: suspend exit
> >
> > The resume entry function has to initialize stack pointer before
> > calling C code, otherwise there will be an external abort occur, in
> > additional, invalidate L1 cache must be done in secure section as
> > well, so this patch also adds assembly code back and keep it as simple as
> possible.
> >
> > Signed-off-by: Anson Huang <anson.hu...@nxp.com>
> > ---
> >  arch/arm/mach-imx/mx7/Makefile   |   2 +-
> >  arch/arm/mach-imx/mx7/psci-mx7.c | 433
> ++++++++++++++++++++++++++++++++++++++-
> >  arch/arm/mach-imx/mx7/psci.S     |  65 ++++++
> >  3 files changed, 497 insertions(+), 3 deletions(-)  create mode
> > 100644 arch/arm/mach-imx/mx7/psci.S
> >
> > diff --git a/arch/arm/mach-imx/mx7/Makefile
> > b/arch/arm/mach-imx/mx7/Makefile index 94a0e64..43c5d92 100644
> > --- a/arch/arm/mach-imx/mx7/Makefile
> > +++ b/arch/arm/mach-imx/mx7/Makefile
> > @@ -4,4 +4,4 @@
> >  #
> >
> >  obj-y      := soc.o clock.o clock_slice.o ddr.o snvs.o
> > -obj-$(CONFIG_ARMV7_PSCI)  += psci-mx7.o
> > +obj-$(CONFIG_ARMV7_PSCI)  += psci-mx7.o psci.o
> > diff --git a/arch/arm/mach-imx/mx7/psci-mx7.c
> > b/arch/arm/mach-imx/mx7/psci-mx7.c
> > index d6c4263..2ed77e1 100644
> > --- a/arch/arm/mach-imx/mx7/psci-mx7.c
> > +++ b/arch/arm/mach-imx/mx7/psci-mx7.c
> > @@ -8,20 +8,77 @@
> >  #include <asm/psci.h>
> >  #include <asm/secure.h>
> >  #include <asm/arch/imx-regs.h>
> > +#include <asm/armv7.h>
> > +#include <asm/gic.h>
> >  #include <linux/bitops.h>
> >  #include <common.h>
> >  #include <fsl_wdog.h>
> >
> > -#define GPC_CPU_PGC_SW_PDN_REQ     0xfc
> > +#define GPC_LPCR_A7_BSC    0x0
> > +#define GPC_LPCR_A7_AD             0x4
> > +#define GPC_SLPCR          0x14
> > +#define GPC_PGC_ACK_SEL_A7 0x24
> > +#define GPC_IMR1_CORE0             0x30
> > +#define GPC_SLOT0_CFG              0xb0
> >  #define GPC_CPU_PGC_SW_PUP_REQ     0xf0
> > +#define GPC_CPU_PGC_SW_PDN_REQ     0xfc
> > +#define GPC_PGC_C0         0x800
> >  #define GPC_PGC_C0         0x800
> >  #define GPC_PGC_C1         0x840
> > +#define GPC_PGC_SCU                0x880
> > +
> > +#define BM_LPCR_A7_BSC_CPU_CLK_ON_LPM              0x4000
> > +#define BM_LPCR_A7_BSC_LPM1                        0xc
> > +#define BM_LPCR_A7_BSC_LPM0                        0x3
> > +#define BP_LPCR_A7_BSC_LPM0                        0
> > +#define BM_SLPCR_EN_DSM                            0x80000000
> > +#define BM_SLPCR_RBC_EN                            0x40000000
> > +#define BM_SLPCR_REG_BYPASS_COUNT          0x3f000000
> > +#define BM_SLPCR_VSTBY                             0x4
> > +#define BM_SLPCR_SBYOS                             0x2
> > +#define BM_SLPCR_BYPASS_PMIC_READY         0x1
> > +#define BM_LPCR_A7_AD_L2PGE                        0x10000
> > +#define BM_LPCR_A7_AD_EN_C1_PUP                    0x800
> > +#define BM_LPCR_A7_AD_EN_C0_PUP                    0x200
> > +#define BM_LPCR_A7_AD_EN_PLAT_PDN          0x10
> > +#define BM_LPCR_A7_AD_EN_C1_PDN                    0x8
> > +#define BM_LPCR_A7_AD_EN_C0_PDN                    0x2
> >
> >  #define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE0_A7 0x1
> >  #define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 0x2
> >
> > -/* below is for i.MX7D */
> > +#define BM_GPC_PGC_ACK_SEL_A7_PD_DUMMY_ACK 0x8000
> > +#define BM_GPC_PGC_ACK_SEL_A7_PU_DUMMY_ACK 0x80000000
> > +
> > +#define MAX_SLOT_NUMBER                            10
> > +#define A7_LPM_WAIT                                0x5
> > +#define A7_LPM_STOP                                0xa
> > +
> > +#define BM_SYS_COUNTER_CNTCR_FCR1 0x200 #define
> > +BM_SYS_COUNTER_CNTCR_FCR0 0x100
> > +
> > +#define REG_SET            0x4
> > +#define REG_CLR            0x8
> > +
> > +#define ANADIG_ARM_PLL             0x60
> > +#define ANADIG_DDR_PLL             0x70
> > +#define ANADIG_SYS_PLL             0xb0
> > +#define ANADIG_ENET_PLL            0xe0
> > +#define ANADIG_AUDIO_PLL   0xf0
> > +#define ANADIG_VIDEO_PLL   0x130
> > +#define BM_ANATOP_ARM_PLL_OVERRIDE BIT(20)
> > +#define BM_ANATOP_DDR_PLL_OVERRIDE BIT(19)
> > +#define BM_ANATOP_SYS_PLL_OVERRIDE (0x1ff << 17)
> > +#define BM_ANATOP_ENET_PLL_OVERRIDE        BIT(13)
> > +#define BM_ANATOP_AUDIO_PLL_OVERRIDE       BIT(24)
> > +#define BM_ANATOP_VIDEO_PLL_OVERRIDE       BIT(24)
> > +
> > +#define DDRC_STAT  0x4
> > +#define DDRC_PWRCTL        0x30
> > +#define DDRC_PSTAT 0x3fc
> > +
> >  #define SRC_GPR1_MX7D              0x074
> > +#define SRC_GPR2_MX7D              0x078
> >  #define SRC_A7RCR0         0x004
> >  #define SRC_A7RCR1         0x008
> >
> > @@ -56,6 +113,27 @@ u8 psci_state[IMX7D_PSCI_NR_CPUS]
> __secure_data = {
> >      PSCI_AFFINITY_LEVEL_ON,
> >      PSCI_AFFINITY_LEVEL_OFF};
> >
> > +enum imx_gpc_slot {
> > +   CORE0_A7,
> > +   CORE1_A7,
> > +   SCU_A7,
> > +   FAST_MEGA_MIX,
> > +   MIPI_PHY,
> > +   PCIE_PHY,
> > +   USB_OTG1_PHY,
> > +   USB_OTG2_PHY,
> > +   USB_HSIC_PHY,
> > +   CORE0_M4,
> > +};
> > +
> > +enum mxc_cpu_pwr_mode {
> > +   RUN,
> > +   WAIT,
> > +   STOP,
> > +};
> > +
> > +extern void psci_system_resume(void);
> > +
> >  static inline void psci_set_state(int cpu, u8 state)  {
> >     psci_state[cpu] = state;
> > @@ -237,7 +315,358 @@ __secure s32 psci_features(u32 __always_unused
> > function_id, u32 psci_fid)
> >     case ARM_PSCI_0_2_FN_SYSTEM_OFF:
> >     case ARM_PSCI_0_2_FN_SYSTEM_RESET:
> >     case ARM_PSCI_1_0_FN_PSCI_FEATURES:
> > +   case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND:
> >             return 0x0;
> >     }
> >     return ARM_PSCI_RET_NI;
> >  }
> > +
> > +__secure void imx_gpcv2_set_lpm_mode(enum mxc_cpu_pwr_mode mode)
> {
> 
> I think this and some functions below can declared static.
 
Agree, I will add static to all of them.

> 
> > +   u32 val1, val2, val3;
> > +
> > +   val1 = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_BSC);
> > +   val2 = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +
> > +   /* all cores' LPM settings must be same */
> > +   val1 &= ~(BM_LPCR_A7_BSC_LPM0 | BM_LPCR_A7_BSC_LPM1);
> > +   val1 |= BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> > +
> > +   val2 &= ~(BM_SLPCR_EN_DSM | BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN
> |
> > +           BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY);
> > +   /*
> > +    * GPC: When improper low-power sequence is used,
> > +    * the SoC enters low power mode before the ARM core executes WFI.
> > +    *
> > +    * Software workaround:
> > +    * 1) Software should trigger IRQ #32 (IOMUX) to be always pending
> > +    *    by setting IOMUX_GPR1_IRQ.
> > +    * 2) Software should then unmask IRQ #32 in GPC before setting GPC
> > +    *    Low-Power mode.
> > +    * 3) Software should mask IRQ #32 right after GPC Low-Power mode
> > +    *    is set.
> > +    */
> > +   switch (mode) {
> > +   case RUN:
> > +           val3 = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0);
> > +           val3 &= ~0x1;
> > +           writel(val3, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0);
> > +           break;
> > +   case WAIT:
> > +           val1 |= A7_LPM_WAIT << BP_LPCR_A7_BSC_LPM0;
> > +           val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> > +           val3 = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0);
> > +           val3 &= ~0x1;
> > +           writel(val3, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0);
> > +           break;
> > +   case STOP:
> > +           val1 |= A7_LPM_STOP << BP_LPCR_A7_BSC_LPM0;
> > +           val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> > +           val2 |= BM_SLPCR_EN_DSM;
> > +           val2 |= BM_SLPCR_SBYOS;
> > +           val2 |= BM_SLPCR_VSTBY;
> > +           val2 |= BM_SLPCR_BYPASS_PMIC_READY;
> > +           val3 = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0);
> > +           val3 |= 0x1;
> > +           writel(val3, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0);
> > +           break;
> > +   default:
> > +           return;
> > +   }
> > +   writel(val1, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_BSC);
> > +   writel(val2, GPC_IPS_BASE_ADDR + GPC_SLPCR); }
> > +
> > +__secure void imx_gpcv2_set_plat_power_gate_by_lpm(bool pdn) {
> > +   u32 val = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD);
> > +
> > +   val &= ~(BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE);
> > +   if (pdn)
> > +           val |= BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE;
> > +
> > +   writel(val, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD); }
> > +
> > +__secure void imx_gpcv2_set_cpu_power_gate_by_lpm(u32 cpu, bool pdn)
> > +{
> > +   u32 val;
> > +
> > +   val = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD);
> > +   if (cpu == 0) {
> > +           if (pdn)
> > +                   val |= BM_LPCR_A7_AD_EN_C0_PDN |
> > +                           BM_LPCR_A7_AD_EN_C0_PUP;
> > +           else
> > +                   val &= ~(BM_LPCR_A7_AD_EN_C0_PDN |
> > +                           BM_LPCR_A7_AD_EN_C0_PUP);
> > +   }
> > +   if (cpu == 1) {
> > +           if (pdn)
> > +                   val |= BM_LPCR_A7_AD_EN_C1_PDN |
> > +                           BM_LPCR_A7_AD_EN_C1_PUP;
> > +           else
> > +                   val &= ~(BM_LPCR_A7_AD_EN_C1_PDN |
> > +                           BM_LPCR_A7_AD_EN_C1_PUP);
> > +   }
> > +   writel(val, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD); }
> > +
> > +__secure void imx_gpcv2_set_slot_ack(u32 index, enum imx_gpc_slot
> m_core,
> > +                                bool mode, bool ack)
> > +{
> > +   u32 val;
> > +
> > +   if (index >= MAX_SLOT_NUMBER)
> > +           return;
> > +
> > +   /* set slot */
> > +   writel(readl(GPC_IPS_BASE_ADDR + GPC_SLOT0_CFG + index * 4) |
> > +           ((mode + 1) << (m_core * 2)),
> > +           GPC_IPS_BASE_ADDR + GPC_SLOT0_CFG + index * 4);
> > +
> > +   if (ack) {
> > +           /* set ack */
> > +           val = readl(GPC_IPS_BASE_ADDR + GPC_PGC_ACK_SEL_A7);
> > +           /* clear dummy ack */
> > +           val &= ~(mode ? BM_GPC_PGC_ACK_SEL_A7_PU_DUMMY_ACK :
> > +                   BM_GPC_PGC_ACK_SEL_A7_PD_DUMMY_ACK);
> > +           val |= 1 << (m_core + (mode ? 16 : 0));
> > +           writel(val, GPC_IPS_BASE_ADDR + GPC_PGC_ACK_SEL_A7);
> > +   }
> > +}
> > +
> > +__secure void imx_system_counter_resume(void) {
> > +   u32 val;
> > +
> > +   val = readl(SYSCNT_CTRL_IPS_BASE_ADDR);
> > +   val &= ~BM_SYS_COUNTER_CNTCR_FCR1;
> > +   val |= BM_SYS_COUNTER_CNTCR_FCR0;
> > +   writel(val, SYSCNT_CTRL_IPS_BASE_ADDR); }
> > +
> > +__secure void imx_system_counter_suspend(void) {
> > +   u32 val;
> > +
> > +   val = readl(SYSCNT_CTRL_IPS_BASE_ADDR);
> > +   val &= ~BM_SYS_COUNTER_CNTCR_FCR0;
> > +   val |= BM_SYS_COUNTER_CNTCR_FCR1;
> > +   writel(val, SYSCNT_CTRL_IPS_BASE_ADDR); }
> > +
> > +__secure void gic_resume(void)
> > +{
> > +   u32 itlinesnr, i;
> > +   u32 gic_dist_addr = GIC400_ARB_BASE_ADDR + GIC_DIST_OFFSET;
> > +
> > +   /* enable the GIC distributor */
> > +   writel(readl(gic_dist_addr + GICD_CTLR) | 0x03,
> > +          gic_dist_addr + GICD_CTLR);
> > +
> > +   /* TYPER[4:0] contains an encoded number of available interrupts */
> > +   itlinesnr = readl(gic_dist_addr + GICD_TYPER) & 0x1f;
> > +
> > +   /* set all bits in the GIC group registers to one to allow access
> > +    * from non-secure state. The first 32 interrupts are private per
> > +    * CPU and will be set later when enabling the GIC for each core
> > +    */
> > +   for (i = 1; i <= itlinesnr; i++)
> > +           writel((u32)-1, gic_dist_addr + GICD_IGROUPRn + 4 * i); }
> > +
> > +static inline void imx_pll_suspend(void) {
> > +   writel(BM_ANATOP_ARM_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_ARM_PLL + REG_SET);
> > +   writel(BM_ANATOP_DDR_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_DDR_PLL + REG_SET);
> > +   writel(BM_ANATOP_SYS_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_SYS_PLL + REG_SET);
> > +   writel(BM_ANATOP_ENET_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_ENET_PLL + REG_SET);
> > +   writel(BM_ANATOP_AUDIO_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_AUDIO_PLL + REG_SET);
> > +   writel(BM_ANATOP_VIDEO_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_VIDEO_PLL + REG_SET); }
> > +
> > +static inline void imx_pll_resume(void) {
> > +   writel(BM_ANATOP_ARM_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_ARM_PLL + REG_CLR);
> > +   writel(BM_ANATOP_DDR_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_DDR_PLL + REG_CLR);
> > +   writel(BM_ANATOP_SYS_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_SYS_PLL + REG_CLR);
> > +   writel(BM_ANATOP_ENET_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_ENET_PLL + REG_CLR);
> > +   writel(BM_ANATOP_AUDIO_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_AUDIO_PLL + REG_CLR);
> > +   writel(BM_ANATOP_VIDEO_PLL_OVERRIDE,
> > +          ANATOP_BASE_ADDR + ANADIG_VIDEO_PLL + REG_CLR); }
> > +
> > +static inline void imx_udelay(u32 usec) {
> > +   u32 freq;
> > +   u64 start, end;
> > +
> > +   asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
> > +   asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start));
> > +   do {
> > +           asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end));
> > +           if ((end - start) > usec * (freq / 1000000))
> > +                   break;
> > +   } while (1);
> > +}
> > +
> > +static inline void imx_ddrc_enter_self_refresh(void) {
> > +   writel(0, DDRC_IPS_BASE_ADDR + DDRC_PWRCTL);
> > +   while (readl(DDRC_IPS_BASE_ADDR + DDRC_PSTAT) & 0x10001)
> > +           ;
> > +
> > +   writel(0x20, DDRC_IPS_BASE_ADDR + DDRC_PWRCTL);
> > +   while ((readl(DDRC_IPS_BASE_ADDR + DDRC_STAT) & 0x23) != 0x23)
> > +           ;
> > +   writel(readl(DDRC_IPS_BASE_ADDR + DDRC_PWRCTL) | 0x8,
> > +          DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); }
> > +
> > +static inline void imx_ddrc_exit_self_refresh(void) {
> > +   writel(0, DDRC_IPS_BASE_ADDR + DDRC_PWRCTL);
> > +   while ((readl(DDRC_IPS_BASE_ADDR + DDRC_STAT) & 0x3) == 0x3)
> > +           ;
> > +   writel(readl(DDRC_IPS_BASE_ADDR + DDRC_PWRCTL) | 0x1,
> > +          DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); }
> > +
> > +__secure void imx_system_resume(void) {
> > +   unsigned int i, val, imr[4], entry;
> > +
> > +   entry = psci_get_target_pc(0);
> > +   imx_ddrc_exit_self_refresh();
> > +   imx_system_counter_resume();
> > +   imx_gpcv2_set_lpm_mode(RUN);
> > +   imx_gpcv2_set_cpu_power_gate_by_lpm(0, false);
> > +   imx_gpcv2_set_plat_power_gate_by_lpm(false);
> > +   imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C0);
> > +   imx_gpcv2_set_m_core_pgc(false, GPC_PGC_SCU);
> > +
> > +   /*
> > +    * need to mask all interrupts in GPC before
> > +    * operating RBC configurations
> > +    */
> > +   for (i = 0; i < 4; i++) {
> > +           imr[i] = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4);
> > +           writel(~0, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4);
> > +   }
> > +
> > +   /* configure RBC enable bit */
> > +   val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +   val &= ~BM_SLPCR_RBC_EN;
> > +   writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +
> > +   /* configure RBC count */
> > +   val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +   val &= ~BM_SLPCR_REG_BYPASS_COUNT;
> > +   writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +
> > +   /*
> > +    * need to delay at least 2 cycles of CKIL(32K)
> > +    * due to hardware design requirement, which is
> > +    * ~61us, here we use 65us for safe
> > +    */
> > +   imx_udelay(65);
> > +
> > +   /* restore GPC interrupt mask settings */
> > +   for (i = 0; i < 4; i++)
> > +           writel(imr[i], GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4);
> > +
> > +   /* initialize gic distributor */
> > +   gic_resume();
> > +   _nonsec_init();
> > +
> > +   /* save cpu0 entry */
> > +   psci_save(0, entry, 0);
> > +   psci_cpu_entry();
> > +}
> > +
> > +__secure void psci_system_suspend(u32 __always_unused function_id,
> > +                             u32 ep, u32 context_id)
> > +{
> > +   u32 gpc_mask[4];
> > +   u32 i, val;
> > +
> > +   psci_save(0, ep, context_id);
> > +   /* overwrite PLL to be controlled by low power mode */
> > +   imx_pll_suspend();
> > +   imx_system_counter_suspend();
> > +   /* set CA7 platform to enter STOP mode */
> > +   imx_gpcv2_set_lpm_mode(STOP);
> > +   /* enable core0/scu power down/up with low power mode */
> > +   imx_gpcv2_set_cpu_power_gate_by_lpm(0, true);
> > +   imx_gpcv2_set_plat_power_gate_by_lpm(true);
> > +   /* time slot settings for core0 and scu */
> > +   imx_gpcv2_set_slot_ack(0, CORE0_A7, false, false);
> > +   imx_gpcv2_set_slot_ack(1, SCU_A7, false, true);
> > +   imx_gpcv2_set_slot_ack(5, SCU_A7, true, false);
> > +   imx_gpcv2_set_slot_ack(6, CORE0_A7, true, true);
> > +   imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C0);
> > +   imx_gpcv2_set_m_core_pgc(true, GPC_PGC_SCU);
> > +   psci_v7_flush_dcache_all();
> > +
> > +   /* disable GIC distributor */
> > +   writel(0, GIC400_ARB_BASE_ADDR + GIC_DIST_OFFSET);
> > +
> > +   imx_ddrc_enter_self_refresh();
> > +
> > +   for (i = 0; i < 4; i++)
> > +           gpc_mask[i] = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i *
> 4);
> > +
> > +   /*
> > +    * enable the RBC bypass counter here
> > +    * to hold off the interrupts. RBC counter
> > +    * = 8 (240us). With this setting, the latency
> > +    * from wakeup interrupt to ARM power up
> > +    * is ~250uS.
> > +    */
> > +   val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +   val &= ~(0x3f << 24);
> > +   val |= (0x8 << 24);
> > +   writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +
> > +   /* enable the counter. */
> > +   val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +   val |= (1 << 30);
> > +   writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR);
> > +
> > +   /* unmask all the GPC interrupts. */
> > +   for (i = 0; i < 4; i++)
> > +           writel(gpc_mask[i], GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i *
> 4);
> > +
> > +   /*
> > +    * now delay for a short while (3usec)
> > +    * ARM is at 1GHz at this point
> > +    * so a short loop should be enough.
> > +    * this delay is required to ensure that
> > +    * the RBC counter can start counting in
> > +    * case an interrupt is already pending
> > +    * or in case an interrupt arrives just
> > +    * as ARM is about to assert DSM_request.
> > +    */
> > +   imx_udelay(3);
> > +
> > +   /* save resume entry and sp in CPU0 GPR registers */
> > +   asm volatile("mov %0, sp" : "=r" (val));
> > +   writel((u32)psci_system_resume, SRC_BASE_ADDR + SRC_GPR1_MX7D);
> > +   writel(val, SRC_BASE_ADDR + SRC_GPR2_MX7D);
> > +
> > +   /* sleep */
> > +   while (1)
> > +           wfi();
> > +}
> > diff --git a/arch/arm/mach-imx/mx7/psci.S
> > b/arch/arm/mach-imx/mx7/psci.S new file mode 100644 index
> > 0000000..8816e77
> > --- /dev/null
> > +++ b/arch/arm/mach-imx/mx7/psci.S
> 
> Can we use a bit better name? E.g. psci-suspend.S
 
It makes sense, cpu suspend is for cpu idle and system suspend, will change the 
name.

> 
> > @@ -0,0 +1,65 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Copyright 2018 NXP
> > + */
> > +
> > +#include <config.h>
> > +#include <linux/linkage.h>
> > +
> > +#include <asm/armv7.h>
> > +#include <asm/psci.h>
> > +
> > +   .pushsection ._secure.text, "ax"
> > +
> > +   .arch_extension sec
> > +
> > +.globl v7_invalidate_l1
> > +v7_invalidate_l1:
> > +       mov     r0, #0
> > +       mcr     p15, 2, r0, c0, c0, 0
> > +       mrc     p15, 1, r0, c0, c0, 0
> > +
> > +       movw    r1, #0x7fff
> > +       and     r2, r1, r0, lsr #13
> > +
> > +       movw    r1, #0x3ff
> > +
> > +       and     r3, r1, r0, lsr #3      @ NumWays - 1
> > +       add     r2, r2, #1              @ NumSets
> > +
> > +       and     r0, r0, #0x7
> > +       add     r0, r0, #4      @ SetShift
> > +
> > +       clz     r1, r3          @ WayShift
> > +       add     r4, r3, #1      @ NumWays
> > +1:     sub     r2, r2, #1      @ NumSets--
> > +       mov     r3, r4          @ Temp = NumWays
> > +2:     subs    r3, r3, #1      @ Temp--
> > +       mov     r5, r3, lsl r1
> > +       mov     r6, r2, lsl r0
> > +       orr     r5, r5, r6      @ Reg =
> (Temp<<WayShift)|(NumSets<<SetShift)
> > +       mcr     p15, 0, r5, c7, c6, 2
> > +       bgt     2b
> > +       cmp     r2, #0
> > +       bgt     1b
> > +       dsb     st
> > +       isb
> > +       mov pc, lr
> 
> This section seems to use spaces instead of tabs...
> 
> Can we maybe reuse existing code, e.g. from
> arch/arm/cpu/armv7/cache_v7_asm.S?

Will use tab instead of space.
The reason why I have to copy the function into secure section is:
the common code in arch/arm/cpu/armv7/cache_v7_asm.S is located
in DDR space and after Linux kernel boot up, this DDR space will be used
and u-boot code section will be broken, so we have to put it in secure
section which is located in OCRAM and Linux kernel will never touch it.

Anson.

> 
> --
> Stefan
> 
> > +
> > +.globl psci_system_resume
> > +psci_system_resume:
> > +   mov     sp, r0
> > +
> > +   /* invalidate L1 I-cache first */
> > +   mov     r6, #0x0
> > +   mcr     p15, 0, r6, c7, c5, 0
> > +   mcr     p15, 0, r6, c7, c5, 6
> > +   /* enable the Icache and branch prediction */
> > +   mov     r6, #0x1800
> > +   mcr     p15, 0, r6, c1, c0, 0
> > +   isb
> > +
> > +   bl      v7_invalidate_l1
> > +   b       imx_system_resume
> > +
> > +   .popsection
_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to