* [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR @ 2022-02-21 20:55 Marek Vasut 2022-02-21 20:56 ` [PATCH v2 2/2] stm32mp: psci: Implement PSCI system suspend and DRAM SSR Marek Vasut 2022-02-23 8:35 ` [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Patrice CHOTARD 0 siblings, 2 replies; 5+ messages in thread From: Marek Vasut @ 2022-02-21 20:55 UTC (permalink / raw) To: u-boot; +Cc: Marek Vasut, Patrick Delaunay, Patrice Chotard Enable DRAM ASR, auto self-refresh, unconditionally. This saves non-trivial amount of power both at runtime and in suspend (on 2x W632GU6NB-15 ~150mW). Signed-off-by: Marek Vasut <marex@denx.de> Cc: Patrick Delaunay <patrick.delaunay@foss.st.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> --- V2: Rebase on latest changes in this driver past v2022.01 --- drivers/ram/stm32mp1/stm32mp1_ddr.c | 25 ++++++++++++++++++++++++ drivers/ram/stm32mp1/stm32mp1_ddr_regs.h | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c index 4d78aa5cb13..04fc8eab909 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c @@ -27,6 +27,8 @@ #define RCC_DDRITFCR_DPHYAPBRST (BIT(17)) #define RCC_DDRITFCR_DPHYRST (BIT(18)) #define RCC_DDRITFCR_DPHYCTLRST (BIT(19)) +#define RCC_DDRITFCR_DDRCKMOD_MASK (0x7 << 20) +#define RCC_DDRITFCR_DDRCKMOD_ASR (0x1 << 20) struct reg_desc { const char *name; @@ -651,6 +653,26 @@ static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, wait_sw_done_ack(ctl); } +static void stm32mp1_asr_enable(struct ddr_info *priv) +{ + struct stm32mp1_ddrctl *ctl = priv->ctl; + + clrsetbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_ASR); + + start_sw_done(ctl); + + setbits_le32(&ctl->hwlpctl, DDRCTRL_HWLPCTL_HW_LP_EN); + writel(DDRCTRL_PWRTMG_POWERDOWN_TO_X32(0x10) | + DDRCTRL_PWRTMG_SELFREF_TO_X32(0x01), + &ctl->pwrtmg); + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_EN); + + setbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + wait_sw_done_ack(ctl); +} + /* board-specific DDR power initializations. */ __weak int board_ddr_power_init(enum ddr_type ddr_type) { @@ -822,6 +844,9 @@ start: stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, config->c_reg.pwrctl); +/* Enable auto-self-refresh, which saves a bit of power at runtime. */ + stm32mp1_asr_enable(priv); + /* enable uMCTL2 AXI port 0 and 1 */ setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h index f1a26e31f6c..42be1ba57c7 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h +++ b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h @@ -265,8 +265,14 @@ struct stm32mp1_ddrphy { #define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) #define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) #define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32(n) (((n) & 0xff) << 16) +#define DDRCTRL_PWRTMG_POWERDOWN_TO_X32(n) ((n) & 0x1f) + +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) + #define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) #define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] stm32mp: psci: Implement PSCI system suspend and DRAM SSR 2022-02-21 20:55 [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Marek Vasut @ 2022-02-21 20:56 ` Marek Vasut 2022-02-23 8:47 ` Patrice CHOTARD 2022-02-23 8:35 ` [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Patrice CHOTARD 1 sibling, 1 reply; 5+ messages in thread From: Marek Vasut @ 2022-02-21 20:56 UTC (permalink / raw) To: u-boot; +Cc: Marek Vasut, Patrick Delaunay, Patrice Chotard Implement PSCI system suspend and placement of DRAM into SSR while the CPUs are in suspend. This saves non-trivial amount of power in suspend, on 2x W632GU6NB-15 ~710mW. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Patrick Delaunay <patrick.delaunay@foss.st.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> --- V2: - Lowercase all the hex values - Sort bit macros in descending order - Use GENMASK even though it seems to only obfuscate the macros - Rename SYSCFG_CMPCR_MPUEN to SYSCFG_CMPENR_MPUEN - Use ARM CP15 timer in secure_udelay() and secure_waitbits() - Restore DDRC PWRCTL after return from SSR - Call ISB/DSB before WFI - Test on STM32MP157C DK2, saves ~420mW here per USB-C power meter --- arch/arm/mach-stm32mp/include/mach/stm32.h | 3 + arch/arm/mach-stm32mp/psci.c | 527 ++++++++++++++++++++- 2 files changed, 519 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index c11a9903f20..47e88fc3dcd 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -16,8 +16,11 @@ */ #define STM32_RCC_BASE 0x50000000 #define STM32_PWR_BASE 0x50001000 +#define STM32_SYSCFG_BASE 0x50020000 #define STM32_DBGMCU_BASE 0x50081000 #define STM32_FMC2_BASE 0x58002000 +#define STM32_DDRCTRL_BASE 0x5A003000 +#define STM32_DDRPHYC_BASE 0x5A004000 #define STM32_TZC_BASE 0x5C006000 #define STM32_ETZPC_BASE 0x5C007000 #define STM32_STGEN_BASE 0x5C008000 diff --git a/arch/arm/mach-stm32mp/psci.c b/arch/arm/mach-stm32mp/psci.c index 155aa79cd5e..86c160987a9 100644 --- a/arch/arm/mach-stm32mp/psci.c +++ b/arch/arm/mach-stm32mp/psci.c @@ -11,19 +11,152 @@ #include <asm/io.h> #include <asm/psci.h> #include <asm/secure.h> +#include <hang.h> #include <linux/bitops.h> -#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xCA7FACE0 -#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xCA7FACE1 - -#define MPIDR_AFF0 GENMASK(7, 0) - -#define RCC_MP_GRSTCSETR (STM32_RCC_BASE + 0x0404) -#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5) -#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4) -#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) - -#define STM32MP1_PSCI_NR_CPUS 2 +/* PWR */ +#define PWR_CR3 0x0c +#define PWR_MPUCR 0x10 + +#define PWR_CR3_DDRSREN BIT(10) +#define PWR_CR3_DDRRETEN BIT(12) + +#define PWR_MPUCR_PDDS BIT(0) +#define PWR_MPUCR_CSTDBYDIS BIT(3) +#define PWR_MPUCR_CSSF BIT(9) + +/* RCC */ +#define RCC_DDRITFCR 0xd8 + +#define RCC_DDRITFCR_DDRC1EN BIT(0) +#define RCC_DDRITFCR_DDRC1LPEN BIT(1) +#define RCC_DDRITFCR_DDRC2EN BIT(2) +#define RCC_DDRITFCR_DDRC2LPEN BIT(3) +#define RCC_DDRITFCR_DDRPHYCEN BIT(4) +#define RCC_DDRITFCR_DDRPHYCLPEN BIT(5) +#define RCC_DDRITFCR_DDRCAPBEN BIT(6) +#define RCC_DDRITFCR_DDRCAPBLPEN BIT(7) +#define RCC_DDRITFCR_AXIDCGEN BIT(8) +#define RCC_DDRITFCR_DDRPHYCAPBEN BIT(9) +#define RCC_DDRITFCR_DDRPHYCAPBLPEN BIT(10) +#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) +#define RCC_DDRITFCR_GSKPCTRL BIT(24) + +#define RCC_MP_SREQSETR 0x104 +#define RCC_MP_SREQCLRR 0x108 + +#define RCC_MP_CIER 0x414 +#define RCC_MP_CIFR 0x418 +#define RCC_MP_CIFR_WKUPF BIT(20) + +/* SYSCFG */ +#define SYSCFG_CMPCR 0x20 +#define SYSCFG_CMPCR_SW_CTRL BIT(2) +#define SYSCFG_CMPENSETR 0x24 +#define SYSCFG_CMPENCLRR 0x28 +#define SYSCFG_CMPENR_MPUEN BIT(0) + +/* DDR Controller registers offsets */ +#define DDRCTRL_STAT 0x004 +#define DDRCTRL_PWRCTL 0x030 +#define DDRCTRL_PWRTMG 0x034 +#define DDRCTRL_HWLPCTL 0x038 +#define DDRCTRL_DFIMISC 0x1b0 +#define DDRCTRL_SWCTL 0x320 +#define DDRCTRL_SWSTAT 0x324 +#define DDRCTRL_PSTAT 0x3fc +#define DDRCTRL_PCTRL_0 0x490 +#define DDRCTRL_PCTRL_1 0x540 + +/* DDR Controller Register fields */ +#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0) +#define DDRCTRL_STAT_OPERATING_MODE_NORMAL 0x1 +#define DDRCTRL_STAT_OPERATING_MODE_SR 0x3 +#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) +#define DDRCTRL_STAT_SELFREF_TYPE_ASR (0x3 << 4) +#define DDRCTRL_STAT_SELFREF_TYPE_SR (0x2 << 4) + +#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) +#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) + +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(23, 16) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) + +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) + +#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) + +#define DDRCTRL_SWCTL_SW_DONE BIT(0) + +#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0) + +#define DDRCTRL_PSTAT_RD_PORT_BUSY_0 BIT(0) +#define DDRCTRL_PSTAT_RD_PORT_BUSY_1 BIT(1) +#define DDRCTRL_PSTAT_WR_PORT_BUSY_0 BIT(16) +#define DDRCTRL_PSTAT_WR_PORT_BUSY_1 BIT(17) + +#define DDRCTRL_PCTRL_N_PORT_EN BIT(0) + +/* DDR PHY registers offsets */ +#define DDRPHYC_PIR 0x004 +#define DDRPHYC_PGSR 0x00c +#define DDRPHYC_ACDLLCR 0x014 +#define DDRPHYC_ACIOCR 0x024 +#define DDRPHYC_DXCCR 0x028 +#define DDRPHYC_DSGCR 0x02c +#define DDRPHYC_ZQ0CR0 0x180 +#define DDRPHYC_DX0DLLCR 0x1cc +#define DDRPHYC_DX1DLLCR 0x20c +#define DDRPHYC_DX2DLLCR 0x24c +#define DDRPHYC_DX3DLLCR 0x28c + +/* DDR PHY Register fields */ +#define DDRPHYC_PIR_INIT BIT(0) +#define DDRPHYC_PIR_DLLSRST BIT(1) +#define DDRPHYC_PIR_DLLLOCK BIT(2) +#define DDRPHYC_PIR_ITMSRST BIT(4) + +#define DDRPHYC_PGSR_IDONE BIT(0) + +#define DDRPHYC_ACDLLCR_DLLSRST BIT(30) +#define DDRPHYC_ACDLLCR_DLLDIS BIT(31) + +#define DDRPHYC_ACIOCR_ACOE BIT(1) +#define DDRPHYC_ACIOCR_ACPDD BIT(3) +#define DDRPHYC_ACIOCR_ACPDR BIT(4) +#define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK(10, 8) +#define DDRPHYC_ACIOCR_CKPDD_0 BIT(8) +#define DDRPHYC_ACIOCR_CKPDR_MASK GENMASK(13, 11) +#define DDRPHYC_ACIOCR_CKPDR_0 BIT(11) +#define DDRPHYC_ACIOCR_CSPDD_MASK GENMASK(20, 18) +#define DDRPHYC_ACIOCR_CSPDD_0 BIT(18) + +#define DDRPHYC_DXCCR_DXPDD BIT(2) +#define DDRPHYC_DXCCR_DXPDR BIT(3) + +#define DDRPHYC_DSGCR_CKEPDD_MASK GENMASK(19, 16) +#define DDRPHYC_DSGCR_CKEPDD_0 BIT(16) +#define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK(23, 20) +#define DDRPHYC_DSGCR_ODTPDD_0 BIT(20) +#define DDRPHYC_DSGCR_NL2PD BIT(24) +#define DDRPHYC_DSGCR_CKOE BIT(28) + +#define DDRPHYC_ZQ0CRN_ZQPD BIT(31) + +#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) + +#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xca7face0 +#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xca7face1 + +#define MPIDR_AFF0 GENMASK(7, 0) + +#define RCC_MP_GRSTCSETR (STM32_RCC_BASE + 0x0404) +#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) +#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4) +#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5) + +#define STM32MP1_PSCI_NR_CPUS 2 #if STM32MP1_PSCI_NR_CPUS > CONFIG_ARMV7_PSCI_NR_CPUS #error "invalid value for CONFIG_ARMV7_PSCI_NR_CPUS" #endif @@ -98,6 +231,7 @@ s32 __secure psci_features(u32 function_id, u32 psci_fid) case ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE: case ARM_PSCI_0_2_FN_SYSTEM_OFF: case ARM_PSCI_0_2_FN_SYSTEM_RESET: + case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND: return 0x0; } return ARM_PSCI_RET_NI; @@ -222,3 +356,374 @@ void __secure psci_system_off(void) while (1) wfi(); } + +static void __secure secure_udelay(unsigned int delay) +{ + u32 freq = cp15_read_cntfrq() / 1000000; + u64 start, end; + + delay *= freq; + + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); + for (;;) { + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); + if ((end - start) > delay) + break; + } +} + +static int __secure secure_waitbits(u32 reg, u32 mask, u32 val) +{ + u32 freq = cp15_read_cntfrq() / 1000000; + u32 delay = 500 * freq; /* 500 us */ + u64 start, end; + u32 tmp; + + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); + for (;;) { + tmp = readl(reg); + tmp &= mask; + if ((tmp & val) == val) + return 0; + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); + if ((end - start) > delay) + return -ETIMEDOUT; + } +} + +static void __secure ddr_sr_mode_ssr(u32 *saved_pwrctl) +{ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1LPEN | RCC_DDRITFCR_DDRC1EN | + RCC_DDRITFCR_DDRC2LPEN | RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRCAPBLPEN | RCC_DDRITFCR_DDRPHYCAPBLPEN | + RCC_DDRITFCR_DDRCAPBEN | RCC_DDRITFCR_DDRPHYCAPBEN | + RCC_DDRITFCR_DDRPHYCEN); + + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_AXIDCGEN | RCC_DDRITFCR_DDRCKMOD_MASK); + + /* Disable HW LP interface of uMCTL2 */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_HWLPCTL, + DDRCTRL_HWLPCTL_HW_LP_EN); + + /* Configure Automatic LP modes of uMCTL2 */ + clrsetbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRTMG, + DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK, + DDRCTRL_PWRTMG_SELFREF_TO_X32_0); + + /* Save PWRCTL register to restart ASR after suspend (if applicable) */ + *saved_pwrctl = readl(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL); + + /* + * Disable Clock disable with LP modes + * (used in RUN mode for LPDDR2 with specific timing). + */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + + /* Disable automatic Self-Refresh mode */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_EN); +} + +static void __secure ddr_sr_mode_restore(u32 saved_pwrctl) +{ + saved_pwrctl &= DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | + DDRCTRL_PWRCTL_SELFREF_EN; + + /* Restore ASR mode in case it was enabled before suspend. */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, saved_pwrctl); +} + +static int __secure ddr_sw_self_refresh_in(void) +{ + int ret; + + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + /* Blocks AXI ports from taking anymore transactions */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + /* + * Waits unit all AXI ports are idle + * Poll PSTAT.rd_port_busy_n = 0 + * Poll PSTAT.wr_port_busy_n = 0 + */ + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_PSTAT, + DDRCTRL_PSTAT_RD_PORT_BUSY_0 | + DDRCTRL_PSTAT_RD_PORT_BUSY_1 | + DDRCTRL_PSTAT_WR_PORT_BUSY_0 | + DDRCTRL_PSTAT_WR_PORT_BUSY_1, 0); + if (ret) + goto pstat_failed; + + /* SW Self-Refresh entry */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); + + /* + * Wait operating mode change in self-refresh mode + * with STAT.operating_mode[1:0]==11. + * Ensure transition to self-refresh was due to software + * by checking also that STAT.selfref_type[1:0]=2. + */ + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_STAT, + DDRCTRL_STAT_OPERATING_MODE_MASK | + DDRCTRL_STAT_SELFREF_TYPE_MASK, + DDRCTRL_STAT_OPERATING_MODE_SR | + DDRCTRL_STAT_SELFREF_TYPE_SR); + if (ret) + goto selfref_sw_failed; + + /* IOs powering down (PUBL registers) */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDR); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDD_MASK, + DDRPHYC_ACIOCR_CKPDD_0); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDR_MASK, + DDRPHYC_ACIOCR_CKPDR_0); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CSPDD_MASK, + DDRPHYC_ACIOCR_CSPDD_0); + + /* Disable command/address output driver */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_ODTPDD_MASK, + DDRPHYC_DSGCR_ODTPDD_0); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_CKEPDD_MASK, + DDRPHYC_DSGCR_CKEPDD_0); + + /* Disable PZQ cell (PUBL register) */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Set latch */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); + + /* Additional delay to avoid early latch */ + secure_udelay(10); + + /* Activate sw retention in PWRCTRL */ + setbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRRETEN); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Disable all DLLs: GLITCH window */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX0DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX1DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX2DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX3DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL output clock */ + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Deactivate all DDR clocks */ + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRCAPBEN | RCC_DDRITFCR_DDRPHYCAPBEN); + + return 0; + +selfref_sw_failed: + /* This bit should be cleared to restore DDR in its previous state */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_SW); + +pstat_failed: + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + return -EINVAL; +}; + +static void __secure ddr_sw_self_refresh_exit(void) +{ + int ret; + + /* Enable all clocks */ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRPHYCEN | RCC_DDRITFCR_DDRPHYCAPBEN | + RCC_DDRITFCR_DDRCAPBEN); + + /* Handshake */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + + /* Mask dfi_init_complete_en */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_DFIMISC, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + + /* Ack */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_SWSTAT, + DDRCTRL_SWSTAT_SW_DONE_ACK, + DDRCTRL_SWSTAT_SW_DONE_ACK); + if (ret) + hang(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Enable all DLLs: GLITCH window */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX0DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX1DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX2DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX3DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + /* Additional delay to avoid early DLL clock switch */ + secure_udelay(50); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST); + + secure_udelay(10); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST); + + /* PHY partial init: (DLL lock and ITM reset) */ + writel(DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_INIT, + STM32_DDRPHYC_BASE + DDRPHYC_PIR); + + /* Need to wait at least 10 clock cycles before accessing PGSR */ + secure_udelay(1); + + /* Pool end of init */ + ret = secure_waitbits(STM32_DDRPHYC_BASE + DDRPHYC_PGSR, + DDRPHYC_PGSR_IDONE, DDRPHYC_PGSR_IDONE); + if (ret) + hang(); + + /* Handshake */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + + /* Unmask dfi_init_complete_en to uMCTL2 */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_DFIMISC, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + + /* Ack */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_SWSTAT, + DDRCTRL_SWSTAT_SW_DONE_ACK, + DDRCTRL_SWSTAT_SW_DONE_ACK); + if (ret) + hang(); + + /* Deactivate sw retention in PWR */ + clrbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRRETEN); + + /* Enable PZQ cell (PUBL register) */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Enable pad drivers */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + + /* Enable command/address output driver */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_CKPDD_MASK); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_CSPDD_MASK); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + /* Release latch */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_ODTPDD_MASK); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKEPDD_MASK); + + /* Remove selfrefresh */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); + + /* Wait operating_mode == normal */ + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_STAT, + DDRCTRL_STAT_OPERATING_MODE_MASK, + DDRCTRL_STAT_OPERATING_MODE_NORMAL); + if (ret) + hang(); + + /* AXI ports are no longer blocked from taking transactions */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, DDRCTRL_PCTRL_N_PORT_EN); + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, DDRCTRL_PCTRL_N_PORT_EN); + + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); +} + +void __secure psci_system_suspend(u32 __always_unused function_id, + u32 ep, u32 context_id) +{ + u32 saved_pwrctl, reg; + + /* Disable IO compensation */ + + /* Place current APSRC/ANSRC into RAPSRC/RANSRC */ + reg = readl(STM32_SYSCFG_BASE + SYSCFG_CMPCR); + reg >>= 8; + reg &= 0xff << 16; + reg |= SYSCFG_CMPCR_SW_CTRL; + writel(reg, STM32_SYSCFG_BASE + SYSCFG_CMPCR); + writel(SYSCFG_CMPENR_MPUEN, STM32_SYSCFG_BASE + SYSCFG_CMPENCLRR); + + writel(RCC_MP_CIFR_WKUPF, STM32_RCC_BASE + RCC_MP_CIFR); + setbits_le32(STM32_RCC_BASE + RCC_MP_CIER, RCC_MP_CIFR_WKUPF); + + setbits_le32(STM32_PWR_BASE + PWR_MPUCR, + PWR_MPUCR_CSSF | PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_PDDS); + + psci_v7_flush_dcache_all(); + ddr_sr_mode_ssr(&saved_pwrctl); + ddr_sw_self_refresh_in(); + setbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRSREN); + writel(0x3, STM32_RCC_BASE + RCC_MP_SREQSETR); + + /* Zzz, enter stop mode */ + asm volatile( + "isb\n" + "dsb\n" + "wfi\n"); + + writel(0x3, STM32_RCC_BASE + RCC_MP_SREQCLRR); + ddr_sw_self_refresh_exit(); + ddr_sr_mode_restore(saved_pwrctl); + + writel(SYSCFG_CMPENR_MPUEN, STM32_SYSCFG_BASE + SYSCFG_CMPENSETR); + clrbits_le32(STM32_SYSCFG_BASE + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); +} -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] stm32mp: psci: Implement PSCI system suspend and DRAM SSR 2022-02-21 20:56 ` [PATCH v2 2/2] stm32mp: psci: Implement PSCI system suspend and DRAM SSR Marek Vasut @ 2022-02-23 8:47 ` Patrice CHOTARD 0 siblings, 0 replies; 5+ messages in thread From: Patrice CHOTARD @ 2022-02-23 8:47 UTC (permalink / raw) To: Marek Vasut, u-boot; +Cc: Patrick Delaunay Hi Marek On 2/21/22 21:56, Marek Vasut wrote: > Implement PSCI system suspend and placement of DRAM into SSR while the > CPUs are in suspend. This saves non-trivial amount of power in suspend, > on 2x W632GU6NB-15 ~710mW. > > Signed-off-by: Marek Vasut <marex@denx.de> > Cc: Patrick Delaunay <patrick.delaunay@foss.st.com> > Cc: Patrice Chotard <patrice.chotard@foss.st.com> > --- > V2: - Lowercase all the hex values > - Sort bit macros in descending order > - Use GENMASK even though it seems to only obfuscate the macros > - Rename SYSCFG_CMPCR_MPUEN to SYSCFG_CMPENR_MPUEN > - Use ARM CP15 timer in secure_udelay() and secure_waitbits() > - Restore DDRC PWRCTL after return from SSR > - Call ISB/DSB before WFI > - Test on STM32MP157C DK2, saves ~420mW here per USB-C power meter > --- > arch/arm/mach-stm32mp/include/mach/stm32.h | 3 + > arch/arm/mach-stm32mp/psci.c | 527 ++++++++++++++++++++- > 2 files changed, 519 insertions(+), 11 deletions(-) > > diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h > index c11a9903f20..47e88fc3dcd 100644 > --- a/arch/arm/mach-stm32mp/include/mach/stm32.h > +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h > @@ -16,8 +16,11 @@ > */ > #define STM32_RCC_BASE 0x50000000 > #define STM32_PWR_BASE 0x50001000 > +#define STM32_SYSCFG_BASE 0x50020000 > #define STM32_DBGMCU_BASE 0x50081000 > #define STM32_FMC2_BASE 0x58002000 > +#define STM32_DDRCTRL_BASE 0x5A003000 > +#define STM32_DDRPHYC_BASE 0x5A004000 > #define STM32_TZC_BASE 0x5C006000 > #define STM32_ETZPC_BASE 0x5C007000 > #define STM32_STGEN_BASE 0x5C008000 > diff --git a/arch/arm/mach-stm32mp/psci.c b/arch/arm/mach-stm32mp/psci.c > index 155aa79cd5e..86c160987a9 100644 > --- a/arch/arm/mach-stm32mp/psci.c > +++ b/arch/arm/mach-stm32mp/psci.c > @@ -11,19 +11,152 @@ > #include <asm/io.h> > #include <asm/psci.h> > #include <asm/secure.h> > +#include <hang.h> > #include <linux/bitops.h> > > -#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xCA7FACE0 > -#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xCA7FACE1 > - > -#define MPIDR_AFF0 GENMASK(7, 0) > - > -#define RCC_MP_GRSTCSETR (STM32_RCC_BASE + 0x0404) > -#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5) > -#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4) > -#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) > - > -#define STM32MP1_PSCI_NR_CPUS 2 > +/* PWR */ > +#define PWR_CR3 0x0c > +#define PWR_MPUCR 0x10 > + > +#define PWR_CR3_DDRSREN BIT(10) > +#define PWR_CR3_DDRRETEN BIT(12) > + > +#define PWR_MPUCR_PDDS BIT(0) > +#define PWR_MPUCR_CSTDBYDIS BIT(3) > +#define PWR_MPUCR_CSSF BIT(9) > + > +/* RCC */ > +#define RCC_DDRITFCR 0xd8 > + > +#define RCC_DDRITFCR_DDRC1EN BIT(0) > +#define RCC_DDRITFCR_DDRC1LPEN BIT(1) > +#define RCC_DDRITFCR_DDRC2EN BIT(2) > +#define RCC_DDRITFCR_DDRC2LPEN BIT(3) > +#define RCC_DDRITFCR_DDRPHYCEN BIT(4) > +#define RCC_DDRITFCR_DDRPHYCLPEN BIT(5) > +#define RCC_DDRITFCR_DDRCAPBEN BIT(6) > +#define RCC_DDRITFCR_DDRCAPBLPEN BIT(7) > +#define RCC_DDRITFCR_AXIDCGEN BIT(8) > +#define RCC_DDRITFCR_DDRPHYCAPBEN BIT(9) > +#define RCC_DDRITFCR_DDRPHYCAPBLPEN BIT(10) > +#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) > +#define RCC_DDRITFCR_GSKPCTRL BIT(24) > + > +#define RCC_MP_SREQSETR 0x104 > +#define RCC_MP_SREQCLRR 0x108 > + > +#define RCC_MP_CIER 0x414 > +#define RCC_MP_CIFR 0x418 > +#define RCC_MP_CIFR_WKUPF BIT(20) > + > +/* SYSCFG */ > +#define SYSCFG_CMPCR 0x20 > +#define SYSCFG_CMPCR_SW_CTRL BIT(2) > +#define SYSCFG_CMPENSETR 0x24 > +#define SYSCFG_CMPENCLRR 0x28 > +#define SYSCFG_CMPENR_MPUEN BIT(0) > + > +/* DDR Controller registers offsets */ > +#define DDRCTRL_STAT 0x004 > +#define DDRCTRL_PWRCTL 0x030 > +#define DDRCTRL_PWRTMG 0x034 > +#define DDRCTRL_HWLPCTL 0x038 > +#define DDRCTRL_DFIMISC 0x1b0 > +#define DDRCTRL_SWCTL 0x320 > +#define DDRCTRL_SWSTAT 0x324 > +#define DDRCTRL_PSTAT 0x3fc > +#define DDRCTRL_PCTRL_0 0x490 > +#define DDRCTRL_PCTRL_1 0x540 > + > +/* DDR Controller Register fields */ > +#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0) > +#define DDRCTRL_STAT_OPERATING_MODE_NORMAL 0x1 > +#define DDRCTRL_STAT_OPERATING_MODE_SR 0x3 > +#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) > +#define DDRCTRL_STAT_SELFREF_TYPE_ASR (0x3 << 4) > +#define DDRCTRL_STAT_SELFREF_TYPE_SR (0x2 << 4) > + > +#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) > +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) > +#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) > + > +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(23, 16) > +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) > + > +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) > + > +#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) > + > +#define DDRCTRL_SWCTL_SW_DONE BIT(0) > + > +#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0) > + > +#define DDRCTRL_PSTAT_RD_PORT_BUSY_0 BIT(0) > +#define DDRCTRL_PSTAT_RD_PORT_BUSY_1 BIT(1) > +#define DDRCTRL_PSTAT_WR_PORT_BUSY_0 BIT(16) > +#define DDRCTRL_PSTAT_WR_PORT_BUSY_1 BIT(17) > + > +#define DDRCTRL_PCTRL_N_PORT_EN BIT(0) > + > +/* DDR PHY registers offsets */ > +#define DDRPHYC_PIR 0x004 > +#define DDRPHYC_PGSR 0x00c > +#define DDRPHYC_ACDLLCR 0x014 > +#define DDRPHYC_ACIOCR 0x024 > +#define DDRPHYC_DXCCR 0x028 > +#define DDRPHYC_DSGCR 0x02c > +#define DDRPHYC_ZQ0CR0 0x180 > +#define DDRPHYC_DX0DLLCR 0x1cc > +#define DDRPHYC_DX1DLLCR 0x20c > +#define DDRPHYC_DX2DLLCR 0x24c > +#define DDRPHYC_DX3DLLCR 0x28c > + > +/* DDR PHY Register fields */ > +#define DDRPHYC_PIR_INIT BIT(0) > +#define DDRPHYC_PIR_DLLSRST BIT(1) > +#define DDRPHYC_PIR_DLLLOCK BIT(2) > +#define DDRPHYC_PIR_ITMSRST BIT(4) > + > +#define DDRPHYC_PGSR_IDONE BIT(0) > + > +#define DDRPHYC_ACDLLCR_DLLSRST BIT(30) > +#define DDRPHYC_ACDLLCR_DLLDIS BIT(31) > + > +#define DDRPHYC_ACIOCR_ACOE BIT(1) > +#define DDRPHYC_ACIOCR_ACPDD BIT(3) > +#define DDRPHYC_ACIOCR_ACPDR BIT(4) > +#define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK(10, 8) > +#define DDRPHYC_ACIOCR_CKPDD_0 BIT(8) > +#define DDRPHYC_ACIOCR_CKPDR_MASK GENMASK(13, 11) > +#define DDRPHYC_ACIOCR_CKPDR_0 BIT(11) > +#define DDRPHYC_ACIOCR_CSPDD_MASK GENMASK(20, 18) > +#define DDRPHYC_ACIOCR_CSPDD_0 BIT(18) > + > +#define DDRPHYC_DXCCR_DXPDD BIT(2) > +#define DDRPHYC_DXCCR_DXPDR BIT(3) > + > +#define DDRPHYC_DSGCR_CKEPDD_MASK GENMASK(19, 16) > +#define DDRPHYC_DSGCR_CKEPDD_0 BIT(16) > +#define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK(23, 20) > +#define DDRPHYC_DSGCR_ODTPDD_0 BIT(20) > +#define DDRPHYC_DSGCR_NL2PD BIT(24) > +#define DDRPHYC_DSGCR_CKOE BIT(28) > + > +#define DDRPHYC_ZQ0CRN_ZQPD BIT(31) > + > +#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) > + > +#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xca7face0 > +#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xca7face1 > + > +#define MPIDR_AFF0 GENMASK(7, 0) > + > +#define RCC_MP_GRSTCSETR (STM32_RCC_BASE + 0x0404) > +#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) > +#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4) > +#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5) > + > +#define STM32MP1_PSCI_NR_CPUS 2 > #if STM32MP1_PSCI_NR_CPUS > CONFIG_ARMV7_PSCI_NR_CPUS > #error "invalid value for CONFIG_ARMV7_PSCI_NR_CPUS" > #endif > @@ -98,6 +231,7 @@ s32 __secure psci_features(u32 function_id, u32 psci_fid) > case ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE: > case ARM_PSCI_0_2_FN_SYSTEM_OFF: > case ARM_PSCI_0_2_FN_SYSTEM_RESET: > + case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND: > return 0x0; > } > return ARM_PSCI_RET_NI; > @@ -222,3 +356,374 @@ void __secure psci_system_off(void) > while (1) > wfi(); > } > + > +static void __secure secure_udelay(unsigned int delay) > +{ > + u32 freq = cp15_read_cntfrq() / 1000000; > + u64 start, end; > + > + delay *= freq; > + > + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); > + for (;;) { > + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); > + if ((end - start) > delay) > + break; > + } > +} > + > +static int __secure secure_waitbits(u32 reg, u32 mask, u32 val) > +{ > + u32 freq = cp15_read_cntfrq() / 1000000; > + u32 delay = 500 * freq; /* 500 us */ > + u64 start, end; > + u32 tmp; > + > + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); > + for (;;) { > + tmp = readl(reg); > + tmp &= mask; > + if ((tmp & val) == val) > + return 0; > + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); > + if ((end - start) > delay) > + return -ETIMEDOUT; > + } > +} > + > +static void __secure ddr_sr_mode_ssr(u32 *saved_pwrctl) > +{ > + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, > + RCC_DDRITFCR_DDRC1LPEN | RCC_DDRITFCR_DDRC1EN | > + RCC_DDRITFCR_DDRC2LPEN | RCC_DDRITFCR_DDRC2EN | > + RCC_DDRITFCR_DDRCAPBLPEN | RCC_DDRITFCR_DDRPHYCAPBLPEN | > + RCC_DDRITFCR_DDRCAPBEN | RCC_DDRITFCR_DDRPHYCAPBEN | > + RCC_DDRITFCR_DDRPHYCEN); > + > + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, > + RCC_DDRITFCR_AXIDCGEN | RCC_DDRITFCR_DDRCKMOD_MASK); > + > + /* Disable HW LP interface of uMCTL2 */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_HWLPCTL, > + DDRCTRL_HWLPCTL_HW_LP_EN); > + > + /* Configure Automatic LP modes of uMCTL2 */ > + clrsetbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRTMG, > + DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK, > + DDRCTRL_PWRTMG_SELFREF_TO_X32_0); > + > + /* Save PWRCTL register to restart ASR after suspend (if applicable) */ > + *saved_pwrctl = readl(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL); > + > + /* > + * Disable Clock disable with LP modes > + * (used in RUN mode for LPDDR2 with specific timing). > + */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, > + DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); > + > + /* Disable automatic Self-Refresh mode */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, > + DDRCTRL_PWRCTL_SELFREF_EN); > +} > + > +static void __secure ddr_sr_mode_restore(u32 saved_pwrctl) > +{ > + saved_pwrctl &= DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | > + DDRCTRL_PWRCTL_SELFREF_EN; > + > + /* Restore ASR mode in case it was enabled before suspend. */ > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, saved_pwrctl); > +} > + > +static int __secure ddr_sw_self_refresh_in(void) > +{ > + int ret; > + > + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); > + > + /* Blocks AXI ports from taking anymore transactions */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, > + DDRCTRL_PCTRL_N_PORT_EN); > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, > + DDRCTRL_PCTRL_N_PORT_EN); > + > + /* > + * Waits unit all AXI ports are idle > + * Poll PSTAT.rd_port_busy_n = 0 > + * Poll PSTAT.wr_port_busy_n = 0 > + */ > + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_PSTAT, > + DDRCTRL_PSTAT_RD_PORT_BUSY_0 | > + DDRCTRL_PSTAT_RD_PORT_BUSY_1 | > + DDRCTRL_PSTAT_WR_PORT_BUSY_0 | > + DDRCTRL_PSTAT_WR_PORT_BUSY_1, 0); > + if (ret) > + goto pstat_failed; > + > + /* SW Self-Refresh entry */ > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); > + > + /* > + * Wait operating mode change in self-refresh mode > + * with STAT.operating_mode[1:0]==11. > + * Ensure transition to self-refresh was due to software > + * by checking also that STAT.selfref_type[1:0]=2. > + */ > + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_STAT, > + DDRCTRL_STAT_OPERATING_MODE_MASK | > + DDRCTRL_STAT_SELFREF_TYPE_MASK, > + DDRCTRL_STAT_OPERATING_MODE_SR | > + DDRCTRL_STAT_SELFREF_TYPE_SR); > + if (ret) > + goto selfref_sw_failed; > + > + /* IOs powering down (PUBL registers) */ > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDR); > + > + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, > + DDRPHYC_ACIOCR_CKPDD_MASK, > + DDRPHYC_ACIOCR_CKPDD_0); > + > + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, > + DDRPHYC_ACIOCR_CKPDR_MASK, > + DDRPHYC_ACIOCR_CKPDR_0); > + > + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, > + DDRPHYC_ACIOCR_CSPDD_MASK, > + DDRPHYC_ACIOCR_CSPDD_0); > + > + /* Disable command/address output driver */ > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); > + > + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, > + DDRPHYC_DSGCR_ODTPDD_MASK, > + DDRPHYC_DSGCR_ODTPDD_0); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); > + > + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, > + DDRPHYC_DSGCR_CKEPDD_MASK, > + DDRPHYC_DSGCR_CKEPDD_0); > + > + /* Disable PZQ cell (PUBL register) */ > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); > + > + /* Set latch */ > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); > + > + /* Additional delay to avoid early latch */ > + secure_udelay(10); > + > + /* Activate sw retention in PWRCTRL */ > + setbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRRETEN); > + > + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ > + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); > + > + /* Disable all DLLs: GLITCH window */ > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLDIS); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX0DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX1DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX2DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX3DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + /* Switch controller clocks (uMCTL2/PUBL) to DLL output clock */ > + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); > + > + /* Deactivate all DDR clocks */ > + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, > + RCC_DDRITFCR_DDRC1EN | RCC_DDRITFCR_DDRC2EN | > + RCC_DDRITFCR_DDRCAPBEN | RCC_DDRITFCR_DDRPHYCAPBEN); > + > + return 0; > + > +selfref_sw_failed: > + /* This bit should be cleared to restore DDR in its previous state */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, > + DDRCTRL_PWRCTL_SELFREF_SW); > + > +pstat_failed: > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, > + DDRCTRL_PCTRL_N_PORT_EN); > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, > + DDRCTRL_PCTRL_N_PORT_EN); > + > + return -EINVAL; > +}; > + > +static void __secure ddr_sw_self_refresh_exit(void) > +{ > + int ret; > + > + /* Enable all clocks */ > + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, > + RCC_DDRITFCR_DDRC1EN | RCC_DDRITFCR_DDRC2EN | > + RCC_DDRITFCR_DDRPHYCEN | RCC_DDRITFCR_DDRPHYCAPBEN | > + RCC_DDRITFCR_DDRCAPBEN); > + > + /* Handshake */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); > + > + /* Mask dfi_init_complete_en */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_DFIMISC, > + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); > + > + /* Ack */ > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); > + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_SWSTAT, > + DDRCTRL_SWSTAT_SW_DONE_ACK, > + DDRCTRL_SWSTAT_SW_DONE_ACK); > + if (ret) > + hang(); > + > + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ > + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); > + > + /* Enable all DLLs: GLITCH window */ > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, > + DDRPHYC_ACDLLCR_DLLDIS); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX0DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX1DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX2DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX3DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); > + > + /* Additional delay to avoid early DLL clock switch */ > + secure_udelay(50); > + > + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ > + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST); > + > + secure_udelay(10); > + > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST); > + > + /* PHY partial init: (DLL lock and ITM reset) */ > + writel(DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | > + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_INIT, > + STM32_DDRPHYC_BASE + DDRPHYC_PIR); > + > + /* Need to wait at least 10 clock cycles before accessing PGSR */ > + secure_udelay(1); > + > + /* Pool end of init */ > + ret = secure_waitbits(STM32_DDRPHYC_BASE + DDRPHYC_PGSR, > + DDRPHYC_PGSR_IDONE, DDRPHYC_PGSR_IDONE); > + if (ret) > + hang(); > + > + /* Handshake */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); > + > + /* Unmask dfi_init_complete_en to uMCTL2 */ > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_DFIMISC, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); > + > + /* Ack */ > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); > + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_SWSTAT, > + DDRCTRL_SWSTAT_SW_DONE_ACK, > + DDRCTRL_SWSTAT_SW_DONE_ACK); > + if (ret) > + hang(); > + > + /* Deactivate sw retention in PWR */ > + clrbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRRETEN); > + > + /* Enable PZQ cell (PUBL register) */ > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); > + > + /* Enable pad drivers */ > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); > + > + /* Enable command/address output driver */ > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_CKPDD_MASK); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_CSPDD_MASK); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); > + > + /* Release latch */ > + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_ODTPDD_MASK); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); > + > + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKEPDD_MASK); > + > + /* Remove selfrefresh */ > + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); > + > + /* Wait operating_mode == normal */ > + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_STAT, > + DDRCTRL_STAT_OPERATING_MODE_MASK, > + DDRCTRL_STAT_OPERATING_MODE_NORMAL); > + if (ret) > + hang(); > + > + /* AXI ports are no longer blocked from taking transactions */ > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, DDRCTRL_PCTRL_N_PORT_EN); > + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, DDRCTRL_PCTRL_N_PORT_EN); > + > + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); > +} > + > +void __secure psci_system_suspend(u32 __always_unused function_id, > + u32 ep, u32 context_id) > +{ > + u32 saved_pwrctl, reg; > + > + /* Disable IO compensation */ > + > + /* Place current APSRC/ANSRC into RAPSRC/RANSRC */ > + reg = readl(STM32_SYSCFG_BASE + SYSCFG_CMPCR); > + reg >>= 8; > + reg &= 0xff << 16; > + reg |= SYSCFG_CMPCR_SW_CTRL; > + writel(reg, STM32_SYSCFG_BASE + SYSCFG_CMPCR); > + writel(SYSCFG_CMPENR_MPUEN, STM32_SYSCFG_BASE + SYSCFG_CMPENCLRR); > + > + writel(RCC_MP_CIFR_WKUPF, STM32_RCC_BASE + RCC_MP_CIFR); > + setbits_le32(STM32_RCC_BASE + RCC_MP_CIER, RCC_MP_CIFR_WKUPF); > + > + setbits_le32(STM32_PWR_BASE + PWR_MPUCR, > + PWR_MPUCR_CSSF | PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_PDDS); > + > + psci_v7_flush_dcache_all(); > + ddr_sr_mode_ssr(&saved_pwrctl); > + ddr_sw_self_refresh_in(); > + setbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRSREN); > + writel(0x3, STM32_RCC_BASE + RCC_MP_SREQSETR); > + > + /* Zzz, enter stop mode */ > + asm volatile( > + "isb\n" > + "dsb\n" > + "wfi\n"); > + > + writel(0x3, STM32_RCC_BASE + RCC_MP_SREQCLRR); > + ddr_sw_self_refresh_exit(); > + ddr_sr_mode_restore(saved_pwrctl); > + > + writel(SYSCFG_CMPENR_MPUEN, STM32_SYSCFG_BASE + SYSCFG_CMPENSETR); > + clrbits_le32(STM32_SYSCFG_BASE + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); > +} Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com> Thanks Patrice ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR 2022-02-21 20:55 [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Marek Vasut 2022-02-21 20:56 ` [PATCH v2 2/2] stm32mp: psci: Implement PSCI system suspend and DRAM SSR Marek Vasut @ 2022-02-23 8:35 ` Patrice CHOTARD 2022-02-24 23:14 ` Marek Vasut 1 sibling, 1 reply; 5+ messages in thread From: Patrice CHOTARD @ 2022-02-23 8:35 UTC (permalink / raw) To: Marek Vasut, u-boot; +Cc: Patrick Delaunay Hi Marek one minor remark On 2/21/22 21:55, Marek Vasut wrote: > Enable DRAM ASR, auto self-refresh, unconditionally. This saves non-trivial > amount of power both at runtime and in suspend (on 2x W632GU6NB-15 ~150mW). > > Signed-off-by: Marek Vasut <marex@denx.de> > Cc: Patrick Delaunay <patrick.delaunay@foss.st.com> > Cc: Patrice Chotard <patrice.chotard@foss.st.com> > --- > V2: Rebase on latest changes in this driver past v2022.01 > --- > drivers/ram/stm32mp1/stm32mp1_ddr.c | 25 ++++++++++++++++++++++++ > drivers/ram/stm32mp1/stm32mp1_ddr_regs.h | 6 ++++++ > 2 files changed, 31 insertions(+) > > diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c > index 4d78aa5cb13..04fc8eab909 100644 > --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c > +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c > @@ -27,6 +27,8 @@ > #define RCC_DDRITFCR_DPHYAPBRST (BIT(17)) > #define RCC_DDRITFCR_DPHYRST (BIT(18)) > #define RCC_DDRITFCR_DPHYCTLRST (BIT(19)) > +#define RCC_DDRITFCR_DDRCKMOD_MASK (0x7 << 20) > +#define RCC_DDRITFCR_DDRCKMOD_ASR (0x1 << 20) #define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) #define RCC_DDRITFCR_DDRCKMOD_ASR BIT(20) > > struct reg_desc { > const char *name; > @@ -651,6 +653,26 @@ static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, > wait_sw_done_ack(ctl); > } > > +static void stm32mp1_asr_enable(struct ddr_info *priv) > +{ > + struct stm32mp1_ddrctl *ctl = priv->ctl; > + > + clrsetbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCKMOD_MASK, > + RCC_DDRITFCR_DDRCKMOD_ASR); > + > + start_sw_done(ctl); > + > + setbits_le32(&ctl->hwlpctl, DDRCTRL_HWLPCTL_HW_LP_EN); > + writel(DDRCTRL_PWRTMG_POWERDOWN_TO_X32(0x10) | > + DDRCTRL_PWRTMG_SELFREF_TO_X32(0x01), > + &ctl->pwrtmg); > + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); > + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_EN); > + > + setbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); > + wait_sw_done_ack(ctl); > +} > + > /* board-specific DDR power initializations. */ > __weak int board_ddr_power_init(enum ddr_type ddr_type) > { > @@ -822,6 +844,9 @@ start: > stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, > config->c_reg.pwrctl); > > +/* Enable auto-self-refresh, which saves a bit of power at runtime. */ > + stm32mp1_asr_enable(priv); > + > /* enable uMCTL2 AXI port 0 and 1 */ > setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); > setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); > diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h > index f1a26e31f6c..42be1ba57c7 100644 > --- a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h > +++ b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h > @@ -265,8 +265,14 @@ struct stm32mp1_ddrphy { > > #define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) > #define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) > +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) > #define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) > > +#define DDRCTRL_PWRTMG_SELFREF_TO_X32(n) (((n) & 0xff) << 16) > +#define DDRCTRL_PWRTMG_POWERDOWN_TO_X32(n) ((n) & 0x1f) > + > +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) > + > #define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) > > #define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com> Thanks Patrice ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR 2022-02-23 8:35 ` [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Patrice CHOTARD @ 2022-02-24 23:14 ` Marek Vasut 0 siblings, 0 replies; 5+ messages in thread From: Marek Vasut @ 2022-02-24 23:14 UTC (permalink / raw) To: Patrice CHOTARD, u-boot; +Cc: Patrick Delaunay On 2/23/22 09:35, Patrice CHOTARD wrote: Hi, > On 2/21/22 21:55, Marek Vasut wrote: >> Enable DRAM ASR, auto self-refresh, unconditionally. This saves non-trivial >> amount of power both at runtime and in suspend (on 2x W632GU6NB-15 ~150mW). >> >> Signed-off-by: Marek Vasut <marex@denx.de> >> Cc: Patrick Delaunay <patrick.delaunay@foss.st.com> >> Cc: Patrice Chotard <patrice.chotard@foss.st.com> >> --- >> V2: Rebase on latest changes in this driver past v2022.01 >> --- >> drivers/ram/stm32mp1/stm32mp1_ddr.c | 25 ++++++++++++++++++++++++ >> drivers/ram/stm32mp1/stm32mp1_ddr_regs.h | 6 ++++++ >> 2 files changed, 31 insertions(+) >> >> diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c >> index 4d78aa5cb13..04fc8eab909 100644 >> --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c >> +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c >> @@ -27,6 +27,8 @@ >> #define RCC_DDRITFCR_DPHYAPBRST (BIT(17)) >> #define RCC_DDRITFCR_DPHYRST (BIT(18)) >> #define RCC_DDRITFCR_DPHYCTLRST (BIT(19)) >> +#define RCC_DDRITFCR_DDRCKMOD_MASK (0x7 << 20) >> +#define RCC_DDRITFCR_DDRCKMOD_ASR (0x1 << 20) > > #define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) > #define RCC_DDRITFCR_DDRCKMOD_ASR BIT(20) btw. I dislike that GENMASK macro, I think it obfuscates the readability and makes it harder to figure out the bitmask at first glance, but that might just be me. [...] ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-02-24 23:16 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-02-21 20:55 [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Marek Vasut 2022-02-21 20:56 ` [PATCH v2 2/2] stm32mp: psci: Implement PSCI system suspend and DRAM SSR Marek Vasut 2022-02-23 8:47 ` Patrice CHOTARD 2022-02-23 8:35 ` [PATCH v2 1/2] ram: stm32mp1: Unconditionally enable ASR Patrice CHOTARD 2022-02-24 23:14 ` Marek Vasut
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox