[U-Boot] [RESEND 3/3] imx: mx7: add system suspend/resume support
Stefan Agner
stefan at agner.ch
Tue Aug 7 11:59:18 UTC 2018
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.Huang at 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.
> + 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
> @@ -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?
--
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
More information about the U-Boot
mailing list