[U-Boot] [RESEND PATCH V2 3/3] imx: mx7: add system suspend/resume support
Stefan Agner
stefan at agner.ch
Wed Aug 8 12:09:09 UTC 2018
On 08.08.2018 11:09, Anson Huang wrote:
> Hi, Stefan
>
> Anson Huang
> Best Regards!
>
>
>> -----Original Message-----
>> From: Stefan Agner [mailto:stefan at agner.ch]
>> Sent: Wednesday, August 8, 2018 5:03 PM
>> To: Anson Huang <anson.huang at nxp.com>
>> Cc: sbabic at denx.de; Fabio Estevam <fabio.estevam at nxp.com>;
>> albert.u.boot at aribaud.net; bryan.odonoghue at linaro.org;
>> uri.mashiach at compulab.co.il; Stefan Agner <stefan.agner at toradex.com>;
>> Peng Fan <peng.fan at nxp.com>; Ye Li <ye.li at nxp.com>; sjg at chromium.org;
>> u-boot at lists.denx.de; dl-linux-imx <linux-imx at nxp.com>
>> Subject: Re: [U-Boot] [RESEND PATCH V2 3/3] imx: mx7: add system
>> suspend/resume support
>>
>> Hi Anson,
>>
>> On 08.08.2018 03:17, 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
>>
>> Gave this a try with a 3.18-rc6 kernel today on our Colibri iMX7, I could
>> successfully suspend and resume using RTC wakealarm.
>>
>> What is a bit weird is that sometimes the SoC seems to consume more power
>> than other times (sometimes 135mW, sometimes 60mW). Any idea where that
>> could come from?
>>
>> In any case, the system seems to wakeup just fine.
>
> I ONLY tried the function with latest 4.18 kernel and did NOT measure
> the power, if the power you saw
> is a board level total power, then I have no idea, maybe you can
> disable all peripheral devices
> in dtb to have another try, could be some peripherals' different state
> consuming different
> power, if you can break down the power rails to VDD_ARM, VDD_SOC and VDDA_1P8,
> then I think you should be able to see the benefit, and from each
> power rails' number,
> maybe we can figure out why, but I think this patch set is mainly for
> function level, for
> power number optimization, we can have patch later, I think.
I checked VDD_ARM, it gets turned off properly. But yeah, could
definitely be related to some peripheral. Just wanted to ask you since
you just worked with it.
I agree, this finding should not hold back the merge of this patch set.
>
> Noted that latest kernel's PCIe driver has resume issue, you have to
> disable PCIe if using 4.18 kernel.
We do not use PCIe by default, so I think it should not have any
influence.
> I suggest you to break down power rails first.
Yeah will have a closer look to all the individual rails. However, due
to other priorities I probably will do this at a later time.
Since suspend/resume does work stable:
Acked-by: Stefan Agner <stefan at agner.ch>
Tested-by: Stefan Agner <stefan at agner.ch>
--
Stefan
>
> Anson.
>>
>> --
>> Stefan
>>
>> >
>> > 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.Huang at nxp.com>
>> > ---
>> > arch/arm/mach-imx/mx7/Makefile | 2 +-
>> > arch/arm/mach-imx/mx7/psci-mx7.c | 449
>> ++++++++++++++++++++++++++++++++++-
>> > arch/arm/mach-imx/mx7/psci-suspend.S | 67 ++++++
>> > 3 files changed, 515 insertions(+), 3 deletions(-) create mode
>> > 100644 arch/arm/mach-imx/mx7/psci-suspend.S
>> >
>> > diff --git a/arch/arm/mach-imx/mx7/Makefile
>> > b/arch/arm/mach-imx/mx7/Makefile index 94a0e64..f1436e2 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-suspend.o
>> > diff --git a/arch/arm/mach-imx/mx7/psci-mx7.c
>> > b/arch/arm/mach-imx/mx7/psci-mx7.c
>> > index d6c4263..34ba0a9 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,374 @@ __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;
>> > }
>> > +
>> > +static __secure void imx_gpcv2_set_lpm_mode(enum mxc_cpu_pwr_mode
>> > +mode) {
>> > + 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); }
>> > +
>> > +static __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); }
>> > +
>> > +static __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); }
>> > +
>> > +static __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);
>> > + }
>> > +}
>> > +
>> > +static __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); }
>> > +
>> > +static __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); }
>> > +
>> > +static __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();
>> > +
>> > + imx_ddrc_enter_self_refresh();
>> > +
>> > + /*
>> > + * e10133: ARM: Boot failure after A7 enters into
>> > + * low-power idle mode
>> > + *
>> > + * Workaround:
>> > + * If both CPU0/CPU1 are IDLE, the last IDLE CPU should
>> > + * disable GIC first, then REG_BYPASS_COUNTER is used
>> > + * to mask wakeup INT, and then execute “wfi” is used to
>> > + * bring the system into power down processing safely.
>> > + * The counter must be enabled as close to the “wfi” state
>> > + * as possible. The following equation can be used to
>> > + * determine the RBC counter value:
>> > + * RBC_COUNT * (1/32K RTC frequency) >=
>> > + * (46 + PDNSCR_SW + PDNSCR_SW2ISO ) ( 1/IPG_CLK frequency ).
>> > + */
>> > +
>> > + /* disable GIC distributor */
>> > + writel(0, GIC400_ARB_BASE_ADDR + GIC_DIST_OFFSET);
>> > +
>> > + 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-suspend.S
>> > b/arch/arm/mach-imx/mx7/psci-suspend.S
>> > new file mode 100644
>> > index 0000000..a21403f
>> > --- /dev/null
>> > +++ b/arch/arm/mach-imx/mx7/psci-suspend.S
>> > @@ -0,0 +1,67 @@
>> > +/* 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
>> > +
>> > +.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
More information about the U-Boot
mailing list