diff --git a/drivers/st/clk/stm32mp1_clk.c b/drivers/st/clk/stm32mp1_clk.c index 0cc87cc..994614f 100644 --- a/drivers/st/clk/stm32mp1_clk.c +++ b/drivers/st/clk/stm32mp1_clk.c @@ -1572,6 +1572,43 @@ mmio_setbits_32(stgen + CNTCR_OFF, CNTCR_EN); } +/******************************************************************************* + * This function determines the number of needed RTC calendar read operations + * to get consistent values (1 or 2 depending on clock frequencies). + * If APB1 frequency is lower than 7 times the RTC one, the software has to + * read the calendar time and date registers twice. + * Returns true if read twice is needed, false else. + ******************************************************************************/ +bool stm32mp1_rtc_get_read_twice(void) +{ + unsigned long apb1_freq; + uint32_t rtc_freq; + uint32_t apb1_div; + uintptr_t rcc_base = stm32mp_rcc_base(); + + switch ((mmio_read_32(rcc_base + RCC_BDCR) & + RCC_BDCR_RTCSRC_MASK) >> RCC_BDCR_RTCSRC_SHIFT) { + case 1: + rtc_freq = stm32mp_clk_get_rate(CK_LSE); + break; + case 2: + rtc_freq = stm32mp_clk_get_rate(CK_LSI); + break; + case 3: + rtc_freq = stm32mp_clk_get_rate(CK_HSE); + rtc_freq /= (mmio_read_32(rcc_base + RCC_RTCDIVR) & + RCC_DIVR_DIV_MASK) + 1U; + break; + default: + panic(); + } + + apb1_div = mmio_read_32(rcc_base + RCC_APB1DIVR) & RCC_APBXDIV_MASK; + apb1_freq = stm32mp_clk_get_rate(CK_MCU) >> apb1_div; + + return apb1_freq < (rtc_freq * 7U); +} + static void stm32mp1_pkcs_config(uint32_t pkcs) { uintptr_t address = stm32mp_rcc_base() + ((pkcs >> 4) & 0xFFFU); diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c index 7d89d02..f5cffd8 100644 --- a/drivers/st/ddr/stm32mp1_ddr.c +++ b/drivers/st/ddr/stm32mp1_ddr.c @@ -29,6 +29,7 @@ #define INVALID_OFFSET 0xFFU +#define TIMESLOT_US_1US 1U #define TIMEOUT_US_1S 1000000U #define DDRCTL_REG(x, y) \ @@ -698,6 +699,83 @@ stm32mp1_wait_sw_done_ack(ctl); } +static void stm32mp1_refresh_cmd(struct stm32mp1_ddrctl *ctl) +{ + uint32_t dbgstat; + + do { + dbgstat = mmio_read_32((uintptr_t)&ctl->dbgstat); + } while ((dbgstat & DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY) != 0U); + + mmio_setbits_32((uintptr_t)&ctl->dbgcmd, DDRCTRL_DBGCMD_RANK0_REFRESH); +} + +/* Refresh compensation by forcing refresh command + * Rule1: Tref should be always < tREFW ? R x tREBW/8 + * Rule2: refcomp = RU(Tref/tREFI) = RU(RxTref/tREFW) + */ +static +void stm32mp1_refresh_compensation(const struct stm32mp1_ddr_config *config, + struct stm32mp1_ddrctl *ctl, + uint64_t start) +{ + uint32_t tck_ps; + uint64_t time_us, tref, trefi, refcomp, i; + + time_us = timeout_init_us(0) - start; + tck_ps = 1000000000U / config->info.speed; + if (tck_ps == 0U) { + return; + } + /* ref = refresh time in tck */ + tref = time_us * 1000000U / tck_ps; + trefi = ((mmio_read_32((uintptr_t)&ctl->rfshtmg) & + DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK) + >> DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT) * 32U; + if (trefi == 0U) { + return; + } + + /* div round up : number of refresh to compensate */ + refcomp = (tref + trefi - 1U) / trefi; + + for (i = 0; i < refcomp; i++) { + stm32mp1_refresh_cmd(ctl); + } +} + +static void stm32mp1_self_refresh_zcal(struct ddr_info *priv, uint32_t zdata) +{ + /* sequence for PUBL I/O Data Retention during Power-Down */ + + /* 10. Override ZQ calibration with previously (pre-retention) + * calibrated values. This is done by writing 1 to ZQ0CRN.ZDEN + * and the override data to ZQ0CRN.ZDATA. + */ + mmio_setbits_32((uintptr_t)&priv->phy->zq0cr0, DDRPHYC_ZQ0CRN_ZDEN); + + mmio_clrsetbits_32((uintptr_t)&priv->phy->zq0cr0, + DDRPHYC_ZQ0CRN_ZDATA_MASK, + zdata << DDRPHYC_ZQ0CRN_ZDATA_SHIFT); + + /* 11. De-assert the PHY_top data retention enable signals + * (ret_en or ret_en_i/ret_en_n_i). + */ + mmio_setbits_32((uintptr_t)(priv->pwr) + PWR_CR3, PWR_CR3_DDRSRDIS); + mmio_clrbits_32((uintptr_t)(priv->pwr) + PWR_CR3, PWR_CR3_DDRRETEN); + + /* 12. Remove ZQ calibration override by writing 0 to ZQ0CRN.ZDEN. */ + mmio_clrbits_32((uintptr_t)&priv->phy->zq0cr0, DDRPHYC_ZQ0CRN_ZDEN); + + /* 13. Trigger ZQ calibration by writing 1 to PIR.INIT + * and '1' to PIR.ZCAL + */ + /* 14. Wait for ZQ calibration to finish by polling a 1 status + * on PGSR.IDONE. + */ + stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_ZCAL); +} + static int board_ddr_power_init(enum ddr_type ddr_type) { if (dt_pmic_status() > 0) { @@ -710,8 +788,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv, struct stm32mp1_ddr_config *config) { - uint32_t pir; + uint32_t pir, ddr_reten; int ret = -EINVAL; + uint64_t time; if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { ret = board_ddr_power_init(STM32MP_DDR3); @@ -730,6 +809,27 @@ VERBOSE("name = %s\n", config->info.name); VERBOSE("speed = %d kHz\n", config->info.speed); VERBOSE("size = 0x%x\n", config->info.size); + if (config->self_refresh) { + VERBOSE("sel-refresh exit (zdata = 0x%x)\n", config->zdata); + } + + /* Check DDR PHY pads retention */ + ddr_reten = mmio_read_32((uint32_t)(priv->pwr) + PWR_CR3) & + PWR_CR3_DDRRETEN; + if (config->self_refresh) { + if (ddr_reten == 0U) { + VERBOSE("self-refresh aborted: no retention\n"); + config->self_refresh = false; + } + } else { + if (ddr_reten != 0U) { + VERBOSE("disable DDR PHY retention\n"); + mmio_setbits_32((uint32_t)(priv->pwr) + PWR_CR3, + PWR_CR3_DDRSRDIS); + mmio_clrbits_32((uint32_t)(priv->pwr) + PWR_CR3, + PWR_CR3_DDRRETEN); + } + } /* DDR INIT SEQUENCE */ @@ -790,6 +890,12 @@ set_reg(priv, REG_TIMING, &config->c_timing); set_reg(priv, REG_MAP, &config->c_map); + /* Keep the controller in self-refresh mode */ + if (config->self_refresh) { + mmio_setbits_32((uintptr_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + } + /* Skip CTRL init, SDRAM init is done by PHY PUBL */ mmio_clrsetbits_32((uintptr_t)&priv->ctl->init0, DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK, @@ -843,8 +949,20 @@ pir |= DDRPHYC_PIR_DRAMRST; /* Only for DDR3 */ } + /* Treat self-refresh exit : hot boot */ + if (config->self_refresh) { + /* DDR in self refresh mode, remove zcal & reset & init */ + pir &= ~(DDRPHYC_PIR_ZCAL & DDRPHYC_PIR_DRAMRST + & DDRPHYC_PIR_DRAMINIT); + pir |= DDRPHYC_PIR_ZCALBYP; + } + stm32mp1_ddrphy_init(priv->phy, pir); + if (config->self_refresh) { + stm32mp1_self_refresh_zcal(priv, config->zdata); + } + /* * 6. SET DFIMISC.dfi_init_complete_en to 1 * Enable quasi-dynamic register programming. @@ -865,6 +983,13 @@ */ /* Wait uMCTL2 ready */ + + /* Trigger self-refresh exit */ + if (config->self_refresh) { + mmio_clrbits_32((uintptr_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + } + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); /* Switch to DLL OFF mode */ @@ -874,6 +999,8 @@ VERBOSE("DDR DQS training : "); + time = timeout_init_us(0); + /* * 8. Disable Auto refresh and power down by setting * - RFSHCTL3.dis_au_refresh = 1 @@ -898,6 +1025,11 @@ /* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training sequence */ stm32mp1_ddrphy_idone_wait(priv->phy); + /* Refresh compensation: forcing refresh command */ + if (config->self_refresh) { + stm32mp1_refresh_compensation(config, priv->ctl, time); + } + /* * 12. set back registers in step 8 to the orginal values if desidered */ diff --git a/drivers/st/ddr/stm32mp1_ddr_helpers.c b/drivers/st/ddr/stm32mp1_ddr_helpers.c index fcb4cfc..2071bb2 100644 --- a/drivers/st/ddr/stm32mp1_ddr_helpers.c +++ b/drivers/st/ddr/stm32mp1_ddr_helpers.c @@ -6,8 +6,14 @@ #include +#include +#include +#include #include #include +#include + +#define TIMEOUT_500US 500U void ddr_enable_clock(void) { @@ -22,3 +28,469 @@ stm32mp1_clk_rcc_regs_unlock(); } + +static void do_sw_handshake(void) +{ + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + mmio_clrbits_32(ddrctrl_base + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); +} + +static void do_sw_ack(void) +{ + uint64_t timeout; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + mmio_setbits_32(ddrctrl_base + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + + timeout = timeout_init_us(TIMEOUT_500US); + while ((mmio_read_32(ddrctrl_base + DDRCTRL_SWSTAT) & + DDRCTRL_SWSTAT_SW_DONE_ACK) == 0U) { + if (timeout_elapsed(timeout)) { + panic(); + } + } +} + +static int ddr_sw_self_refresh_in(void) +{ + uint64_t timeout; + uint32_t stat; + uint32_t operating_mode; + uint32_t selref_type; + uint8_t op_mode_changed = 0; + uintptr_t rcc_base = stm32mp_rcc_base(); + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + uintptr_t ddrphyc_base = stm32mp_ddrphyc_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_clk_rcc_regs_unlock(); + + /* Blocks AXI ports from taking anymore transactions */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + mmio_clrbits_32(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 + */ + timeout = timeout_init_us(TIMEOUT_500US); + while (mmio_read_32(ddrctrl_base + DDRCTRL_PSTAT)) { + if (timeout_elapsed(timeout)) { + goto pstat_failed; + } + } + /* SW Self-Refresh entry */ + mmio_setbits_32(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. + */ + timeout = timeout_init_us(TIMEOUT_500US); + while (!timeout_elapsed(timeout)) { + stat = mmio_read_32(ddrctrl_base + DDRCTRL_STAT); + operating_mode = stat & DDRCTRL_STAT_OPERATING_MODE_MASK; + selref_type = stat & DDRCTRL_STAT_SELFREF_TYPE_MASK; + + if ((operating_mode == DDRCTRL_STAT_OPERATING_MODE_SR) && + (selref_type == DDRCTRL_STAT_SELFREF_TYPE_SR)) { + op_mode_changed = 1; + break; + } + } + + if (op_mode_changed == 0U) + goto selfref_sw_failed; + + /* IOs powering down (PUBL registers) */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDR); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDD_MASK, + DDRPHYC_ACIOCR_CKPDD_0); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDR_MASK, + DDRPHYC_ACIOCR_CKPDR_0); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CSPDD_MASK, + DDRPHYC_ACIOCR_CSPDD_0); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_ODTPDD_MASK, + DDRPHYC_DSGCR_ODTPDD_0); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_CKEPDD_MASK, + DDRPHYC_DSGCR_CKEPDD_0); + + /* Disable PZQ cell (PUBL register) */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Activate sw retention in PWRCTRL */ + stm32mp_pwr_regs_lock(); + mmio_setbits_32(pwr_base + PWR_CR3, PWR_CR3_DDRRETEN); + stm32mp_pwr_regs_unlock(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + stm32mp1_clk_rcc_regs_lock(); + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + stm32mp1_clk_rcc_regs_unlock(); + + /* Disable all DLLs: GLITCH window */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX0DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX1DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX2DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX3DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + stm32mp1_clk_rcc_regs_lock(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL output clock */ + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Deactivate all DDR clocks */ + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | + RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRCAPBEN | + RCC_DDRITFCR_DDRPHYCAPBEN); + + stm32mp1_clk_rcc_regs_unlock(); + + return 0; + +selfref_sw_failed: + /* This bit should be cleared to restore DDR in its previous state */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_SW); + +pstat_failed: + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + return -1; +} + +int ddr_sw_self_refresh_exit(void) +{ + uint64_t timeout; + uintptr_t rcc_base = stm32mp_rcc_base(); + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + uintptr_t ddrphyc_base = stm32mp_ddrphyc_base(); + + /* Enable all clocks */ + ddr_enable_clock(); + + do_sw_handshake(); + + /* Mask dfi_init_complete_en */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_DFIMISC, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + + do_sw_ack(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + stm32mp1_clk_rcc_regs_lock(); + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + stm32mp1_clk_rcc_regs_unlock(); + + /* Enable all DLLs: GLITCH window */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX0DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX1DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX2DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX3DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + /* Additional delay to avoid early DLL clock switch */ + udelay(10); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + stm32mp1_clk_rcc_regs_lock(); + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + stm32mp1_clk_rcc_regs_unlock(); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLSRST); + + udelay(10); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLSRST); + + /* PHY partial init: (DLL lock and ITM reset) */ + mmio_write_32(ddrphyc_base + DDRPHYC_PIR, + DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_INIT); + + /* Need to wait at least 10 clock cycles before accessing PGSR */ + udelay(1); + + /* Pool end of init */ + timeout = timeout_init_us(TIMEOUT_500US); + + while ((mmio_read_32(ddrphyc_base + DDRPHYC_PGSR) & + DDRPHYC_PGSR_IDONE) == 0U) { + if (timeout_elapsed(timeout)) { + return -1; + } + } + + do_sw_handshake(); + + /* Unmask dfi_init_complete_en to uMCTL2 */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_DFIMISC, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + + do_sw_ack(); + + /* Deactivate sw retention in PWR */ + stm32mp_pwr_regs_lock(); + mmio_clrbits_32(pwr_base + PWR_CR3, PWR_CR3_DDRRETEN); + stm32mp_pwr_regs_unlock(); + + /* Enable PZQ cell (PUBL register) */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Enable pad drivers */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDD_MASK); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CSPDD_MASK); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_ODTPDD_MASK); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_CKEPDD_MASK); + + /* Remove selfrefresh */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_SW); + + /* Wait operating_mode == normal */ + timeout = timeout_init_us(TIMEOUT_500US); + while ((mmio_read_32(ddrctrl_base + DDRCTRL_STAT) & + DDRCTRL_STAT_OPERATING_MODE_MASK) != + DDRCTRL_STAT_OPERATING_MODE_NORMAL) { + if (timeout_elapsed(timeout)) { + return -1; + } + } + + /* AXI ports are no longer blocked from taking transactions */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_clk_rcc_regs_unlock(); + + return 0; +} + +int ddr_standby_sr_entry(uint32_t *zq0cr0_zdata) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t ddrphyc_base = stm32mp_ddrphyc_base(); + + /* Save IOs calibration values */ + if (zq0cr0_zdata != NULL) { + *zq0cr0_zdata = mmio_read_32(ddrphyc_base + DDRPHYC_ZQ0CR0) & + DDRPHYC_ZQ0CRN_ZDATA_MASK; + } + + /* Put DDR in Self-Refresh */ + if (ddr_sw_self_refresh_in() != 0) { + return -1; + } + + /* Enable I/O retention mode in standby */ + stm32mp_pwr_regs_lock(); + mmio_setbits_32(pwr_base + PWR_CR3, PWR_CR3_DDRSREN); + stm32mp_pwr_regs_unlock(); + + return 0; +} + +void ddr_sr_mode_ssr(void) +{ + uintptr_t rcc_ddritfcr = stm32mp_rcc_base() + RCC_DDRITFCR; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1EN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2EN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCAPBLPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCAPBLPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCAPBEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCAPBEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK); + + stm32mp1_clk_rcc_regs_unlock(); + + /* Disable HW LP interface of uMCTL2 */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_HWLPCTL, + DDRCTRL_HWLPCTL_HW_LP_EN); + + /* Configure Automatic LP modes of uMCTL2 */ + mmio_clrsetbits_32(ddrctrl_base + DDRCTRL_PWRTMG, + DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK, + DDRCTRL_PWRTMG_SELFREF_TO_X32_0); + + /* + * Disable Clock disable with LP modes + * (used in RUN mode for LPDDR2 with specific timing). + */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + + /* Disable automatic Self-Refresh mode */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_EN); +} + +void ddr_sr_mode_asr(void) +{ + uintptr_t rcc_ddritfcr = stm32mp_rcc_base() + RCC_DDRITFCR; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN); + + mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_ASR1); + + stm32mp1_clk_rcc_regs_unlock(); + + /* Enable HW LP interface of uMCTL2 */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_HWLPCTL, + DDRCTRL_HWLPCTL_HW_LP_EN); + + /* Configure Automatic LP modes of uMCTL2 */ + mmio_clrsetbits_32(ddrctrl_base + DDRCTRL_PWRTMG, + DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK, + DDRCTRL_PWRTMG_SELFREF_TO_X32_0); + + /* + * Enable Clock disable with LP modes + * (used in RUN mode for LPDDR2 with specific timing). + */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + + /* Enable automatic Self-Refresh for ASR mode */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_EN); +} + +void ddr_sr_mode_hsr(void) +{ + uintptr_t rcc_ddritfcr = stm32mp_rcc_base() + RCC_DDRITFCR; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN); + + mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_HSR1); + + stm32mp1_clk_rcc_regs_unlock(); + + /* Enable HW LP interface of uMCTL2 */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_HWLPCTL, + DDRCTRL_HWLPCTL_HW_LP_EN); + + /* Configure Automatic LP modes of uMCTL2 */ + mmio_clrsetbits_32(ddrctrl_base + DDRCTRL_PWRTMG, + DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK, + DDRCTRL_PWRTMG_SELFREF_TO_X32_0); + + /* + * Enable Clock disable with LP modes + * (used in RUN mode for LPDDR2 with specific timing). + */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); +} diff --git a/drivers/st/ddr/stm32mp1_ram.c b/drivers/st/ddr/stm32mp1_ram.c index 4ae55fc..f94980c 100644 --- a/drivers/st/ddr/stm32mp1_ram.c +++ b/drivers/st/ddr/stm32mp1_ram.c @@ -50,6 +50,26 @@ } /******************************************************************************* + * This function tests a simple read/write access to the DDR. + * Note that the previous content is restored after test. + * Returns 0 if success, and address value else. + ******************************************************************************/ +static uint32_t ddr_test_rw_access(void) +{ + uint32_t saved_value = mmio_read_32(STM32MP_DDR_BASE); + + mmio_write_32(STM32MP_DDR_BASE, DDR_PATTERN); + + if (mmio_read_32(STM32MP_DDR_BASE) != DDR_PATTERN) { + return (uint32_t)STM32MP_DDR_BASE; + } + + mmio_write_32(STM32MP_DDR_BASE, saved_value); + + return 0; +} + +/******************************************************************************* * This function tests the DDR data bus wiring. * This is inspired from the Data Bus Test algorithm written by Michael Barr * in "Programming Embedded Systems in C and C++" book. @@ -168,8 +188,12 @@ int ret; struct stm32mp1_ddr_config config; int node, len; - uint32_t uret, idx; + uint32_t magic, uret, idx; void *fdt; + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); #define PARAM(x, y) \ { \ @@ -237,6 +261,18 @@ } } + config.self_refresh = false; + + stm32mp_clk_enable(RTCAPB); + + magic = mmio_read_32(bkpr_core1_magic); + if (magic == BOOT_API_A7_CORE0_MAGIC_NUMBER) { + config.self_refresh = true; + config.zdata = stm32_get_zdata_from_context(); + } + + stm32mp_clk_disable(RTCAPB); + /* Disable axidcg clock gating during init */ mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); @@ -245,6 +281,14 @@ /* Enable axidcg clock gating */ mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + /* check if DDR content is lost (self-refresh aborted) */ + if ((magic == BOOT_API_A7_CORE0_MAGIC_NUMBER) && !config.self_refresh) { + /* clear Backup register */ + mmio_write_32(bkpr_core1_addr, 0); + /* clear magic number */ + mmio_write_32(bkpr_core1_magic, 0); + } + priv->info.size = config.info.size; VERBOSE("%s : ram size(%x, %x)\n", __func__, @@ -253,25 +297,37 @@ write_sctlr(read_sctlr() & ~SCTLR_C_BIT); dcsw_op_all(DC_OP_CISW); - uret = ddr_test_data_bus(); - if (uret != 0U) { - ERROR("DDR data bus test: can't access memory @ 0x%x\n", - uret); - panic(); - } + if (config.self_refresh) { + uret = ddr_test_rw_access(); + if (uret != 0U) { + ERROR("DDR rw test: Can't access memory @ 0x%x\n", + uret); + panic(); + } - uret = ddr_test_addr_bus(); - if (uret != 0U) { - ERROR("DDR addr bus test: can't access memory @ 0x%x\n", - uret); - panic(); - } + /* Restore area overwritten by training */ + stm32_restore_ddr_training_area(); + } else { + uret = ddr_test_data_bus(); + if (uret != 0U) { + ERROR("DDR data bus test: can't access memory @ 0x%x\n", + uret); + panic(); + } - uret = ddr_check_size(); - if (uret < config.info.size) { - ERROR("DDR size: 0x%x does not match DT config: 0x%x\n", - uret, config.info.size); - panic(); + uret = ddr_test_addr_bus(); + if (uret != 0U) { + ERROR("DDR addr bus test: can't access memory @ 0x%x\n", + uret); + panic(); + } + + uret = ddr_check_size(); + if (uret < config.info.size) { + ERROR("DDR size: 0x%x does not match DT config: 0x%x\n", + uret, config.info.size); + panic(); + } } write_sctlr(read_sctlr() | SCTLR_C_BIT); diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index 9e9dddc..dc44b73 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -27,6 +27,8 @@ #define STPMIC1_BUCK_OUTPUT_SHIFT 2 #define STPMIC1_BUCK3_1V8 (39U << STPMIC1_BUCK_OUTPUT_SHIFT) +#define REGULATOR_MODE_STANDBY 8U + #define STPMIC1_DEFAULT_START_UP_DELAY_MS 1 static struct i2c_handle_s i2c_handle; @@ -174,6 +176,99 @@ return 0; } +int dt_pmic_set_lp_config(const char *node_name) +{ + int pmic_node, regulators_node, regulator_node; + int status; + void *fdt; + + if (node_name == NULL) { + return 0; + } + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + status = stpmic1_powerctrl_on(); + if (status != 0) { + return status; + }; + + regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); + + fdt_for_each_subnode(regulator_node, fdt, regulators_node) { + const fdt32_t *cuint; + const char *reg_name; + int regulator_state_node; + + /* + * First, copy active configuration (Control register) to + * PWRCTRL Control register, even if regulator_state_node + * does not exist. + */ + reg_name = fdt_get_name(fdt, regulator_node, NULL); + status = stpmic1_lp_copy_reg(reg_name); + if (status != 0) { + return status; + } + + /* Then apply configs from regulator_state_node */ + regulator_state_node = fdt_subnode_offset(fdt, + regulator_node, + node_name); + if (regulator_state_node <= 0) { + continue; + } + + if (fdt_getprop(fdt, regulator_state_node, + "regulator-on-in-suspend", NULL) != NULL) { + status = stpmic1_lp_reg_on_off(reg_name, 1); + if (status != 0) { + return status; + } + } + + if (fdt_getprop(fdt, regulator_state_node, + "regulator-off-in-suspend", NULL) != NULL) { + status = stpmic1_lp_reg_on_off(reg_name, 0); + if (status != 0) { + return status; + } + } + + cuint = fdt_getprop(fdt, regulator_state_node, + "regulator-suspend-microvolt", NULL); + if (cuint != NULL) { + uint16_t voltage = (uint16_t)(fdt32_to_cpu(*cuint) / + 1000U); + + status = stpmic1_lp_set_voltage(reg_name, voltage); + if (status != 0) { + return status; + } + } + + cuint = fdt_getprop(fdt, regulator_state_node, + "regulator-mode", NULL); + if (cuint != NULL) { + if (fdt32_to_cpu(*cuint) == REGULATOR_MODE_STANDBY) { + status = stpmic1_lp_set_mode(reg_name, 1); + if (status != 0) { + return status; + } + } + } + } + + return 0; +} + bool initialize_pmic_i2c(void) { int ret; diff --git a/drivers/st/pmic/stpmic1.c b/drivers/st/pmic/stpmic1.c index 9999630..51754fc 100644 --- a/drivers/st/pmic/stpmic1.c +++ b/drivers/st/pmic/stpmic1.c @@ -648,6 +648,58 @@ regul->mask_reset); } +/* Low-power functions */ +int stpmic1_lp_copy_reg(const char *name) +{ + uint8_t val; + int status; + const struct regul_struct *regul = get_regulator_data(name); + + status = stpmic1_register_read(regul->control_reg, &val); + if (status != 0) { + return status; + } + + return stpmic1_register_write(regul->low_power_reg, val); +} + +int stpmic1_lp_reg_on_off(const char *name, uint8_t enable) +{ + const struct regul_struct *regul = get_regulator_data(name); + + return stpmic1_register_update(regul->low_power_reg, enable, + LDO_BUCK_ENABLE_MASK); +} + +int stpmic1_lp_set_mode(const char *name, uint8_t hplp) +{ + const struct regul_struct *regul = get_regulator_data(name); + + return stpmic1_register_update(regul->low_power_reg, + hplp << LDO_BUCK_HPLP_SHIFT, + LDO_BUCK_HPLP_ENABLE_MASK); +} + +int stpmic1_lp_set_voltage(const char *name, uint16_t millivolts) +{ + uint8_t voltage_index = voltage_to_index(name, millivolts); + const struct regul_struct *regul = get_regulator_data(name); + uint8_t mask; + + /* Voltage can be set for buck or ldo (except ldo4) regulators */ + if (strncmp(name, "buck", 4) == 0) { + mask = BUCK_VOLTAGE_MASK; + } else if ((strncmp(name, "ldo", 3) == 0) && + (strncmp(name, "ldo4", 4) != 0)) { + mask = LDO_VOLTAGE_MASK; + } else { + return 0; + } + + return stpmic1_register_update(regul->low_power_reg, voltage_index << 2, + mask); +} + int stpmic1_regulator_voltage_get(const char *name) { const struct regul_struct *regul = get_regulator_data(name); diff --git a/drivers/st/rtc/stm32_rtc.c b/drivers/st/rtc/stm32_rtc.c new file mode 100644 index 0000000..eaa6f75 --- /dev/null +++ b/drivers/st/rtc/stm32_rtc.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define RTC_COMPAT "st,stm32mp1-rtc" + +#define RTC_TR_SU_MASK GENMASK(3, 0) +#define RTC_TR_ST_MASK GENMASK(6, 4) +#define RTC_TR_ST_SHIFT 4 +#define RTC_TR_MNU_MASK GENMASK(11, 8) +#define RTC_TR_MNU_SHIFT 8 +#define RTC_TR_MNT_MASK GENMASK(14, 12) +#define RTC_TR_MNT_SHIFT 12 +#define RTC_TR_HU_MASK GENMASK(19, 16) +#define RTC_TR_HU_SHIFT 16 +#define RTC_TR_HT_MASK GENMASK(21, 20) +#define RTC_TR_HT_SHIFT 20 +#define RTC_TR_PM BIT(22) + +#define RTC_DR_DU_MASK GENMASK(3, 0) +#define RTC_DR_DT_MASK GENMASK(5, 4) +#define RTC_DR_DT_SHIFT 4 +#define RTC_DR_MU_MASK GENMASK(11, 8) +#define RTC_DR_MU_SHIFT 8 +#define RTC_DR_MT BIT(12) +#define RTC_DR_MT_SHIFT 12 +#define RTC_DR_WDU_MASK GENMASK(15, 13) +#define RTC_DR_WDU_SHIFT 13 +#define RTC_DR_YU_MASK GENMASK(19, 16) +#define RTC_DR_YU_SHIFT 16 +#define RTC_DR_YT_MASK GENMASK(23, 20) +#define RTC_DR_YT_SHIFT 20 + +#define RTC_SSR_SS_MASK GENMASK(15, 0) + +#define RTC_ICSR_ALRAWF BIT(0) +#define RTC_ICSR_RSF BIT(5) + +#define RTC_PRER_PREDIV_S_MASK GENMASK(14, 0) + +#define RTC_CR_BYPSHAD BIT(5) +#define RTC_CR_BYPSHAD_SHIFT 5 +#define RTC_CR_ALRAE BIT(8) +#define RTC_CR_ALRAIE BIT(12) +#define RTC_CR_TAMPTS BIT(25) + +#define RTC_SMCR_TS_DPROT BIT(3) + +#define RTC_TSDR_DU_MASK GENMASK(3, 0) +#define RTC_TSDR_DU_SHIFT 0 +#define RTC_TSDR_DT_MASK GENMASK(5, 4) +#define RTC_TSDR_DT_SHIFT 4 +#define RTC_TSDR_MU_MASK GENMASK(11, 8) +#define RTC_TSDR_MU_SHIFT 8 + +#define RTC_ALRMAR_DU_SHIFT 24 + +#define RTC_SR_TSF BIT(3) +#define RTC_SR_TSOVF BIT(4) + +#define RTC_SCR_CTSF BIT(3) +#define RTC_SCR_CTSOVF BIT(4) + +#define RTC_WPR_KEY1 0xCA +#define RTC_WPR_KEY2 0x53 +#define RTC_WPR_KEY_LOCK 0xFF + +static struct dt_node_info rtc_dev; + +static struct spinlock lock; + +void stm32_rtc_regs_lock(void) +{ + if (stm32mp_lock_available()) { + spin_lock(&lock); + } +} + +void stm32_rtc_regs_unlock(void) +{ + if (stm32mp_lock_available()) { + spin_unlock(&lock); + } +} + +static void stm32_rtc_write_unprotect(void) +{ + mmio_write_32(rtc_dev.base + RTC_WPR, RTC_WPR_KEY1); + mmio_write_32(rtc_dev.base + RTC_WPR, RTC_WPR_KEY2); +} + +static void stm32_rtc_write_protect(void) +{ + mmio_write_32(rtc_dev.base + RTC_WPR, RTC_WPR_KEY_LOCK); +} + +/******************************************************************************* + * This function gets the BYPSHAD bit value of the RTC_CR register. + * It will determine if we need to reset RTC_ISCR.RSF after each RTC calendar + * read, and also wait for RTC_ISCR.RSF=1 before next read. + * Returns true or false depending on the bit value. + ******************************************************************************/ +static bool stm32_rtc_get_bypshad(void) +{ + return ((mmio_read_32(rtc_dev.base + RTC_CR) & RTC_CR_BYPSHAD) >> + RTC_CR_BYPSHAD_SHIFT) != 0U; +} + +/******************************************************************************* + * This function reads the RTC calendar register values. + * If shadow registers are not bypassed, then a reset/poll is done. + ******************************************************************************/ +static void stm32_rtc_read_calendar(struct stm32_rtc_calendar *calendar) +{ + bool bypshad = stm32_rtc_get_bypshad(); + + if (!bypshad) { + mmio_clrbits_32((uint32_t)(rtc_dev.base + RTC_ICSR), + RTC_ICSR_RSF); + while ((mmio_read_32(rtc_dev.base + RTC_ICSR) & RTC_ICSR_RSF) != + RTC_ICSR_RSF) { + ; + } + } + + calendar->ssr = mmio_read_32(rtc_dev.base + RTC_SSR); + calendar->tr = mmio_read_32(rtc_dev.base + RTC_TR); + calendar->dr = mmio_read_32(rtc_dev.base + RTC_DR); +} + +/******************************************************************************* + * This function fill the rtc_time structure based on rtc_calendar register. + ******************************************************************************/ +static void stm32_rtc_get_time(struct stm32_rtc_calendar *cal, + struct stm32_rtc_time *tm) +{ + assert(cal != NULL); + assert(tm != NULL); + + tm->hour = (((cal->tr & RTC_TR_HT_MASK) >> RTC_TR_HT_SHIFT) * 10U) + + ((cal->tr & RTC_TR_HU_MASK) >> RTC_TR_HU_SHIFT); + + if ((cal->tr & RTC_TR_PM) != 0U) { + tm->hour += 12U; + } + + tm->min = (((cal->tr & RTC_TR_MNT_MASK) >> RTC_TR_MNT_SHIFT) * 10U) + + ((cal->tr & RTC_TR_MNU_MASK) >> RTC_TR_MNU_SHIFT); + tm->sec = (((cal->tr & RTC_TR_ST_MASK) >> RTC_TR_ST_SHIFT) * 10U) + + (cal->tr & RTC_TR_SU_MASK); +} + +/******************************************************************************* + * This function fill the rtc_time structure with the given date register. + ******************************************************************************/ +static void stm32_rtc_get_date(struct stm32_rtc_calendar *cal, + struct stm32_rtc_time *tm) +{ + assert(cal != NULL); + assert(tm != NULL); + + tm->wday = (((cal->dr & RTC_DR_WDU_MASK) >> RTC_DR_WDU_SHIFT)); + + tm->day = (((cal->dr & RTC_DR_DT_MASK) >> RTC_DR_DT_SHIFT) * 10U) + + (cal->dr & RTC_DR_DU_MASK); + + tm->month = (((cal->dr & RTC_DR_MT) >> RTC_DR_MT_SHIFT) * 10U) + + ((cal->dr & RTC_DR_MU_MASK) >> RTC_DR_MU_SHIFT); + + tm->year = (((cal->dr & RTC_DR_YT_MASK) >> RTC_DR_YT_SHIFT) * 10U) + + ((cal->dr & RTC_DR_YU_MASK) >> RTC_DR_YU_SHIFT) + 2000U; +} + +/******************************************************************************* + * This function reads the RTC timestamp register values and update time + * structure with the corresponding value. + ******************************************************************************/ +static void stm32_rtc_read_timestamp(struct stm32_rtc_time *time) +{ + assert(time != NULL); + + struct stm32_rtc_calendar cal_tamp; + + cal_tamp.tr = mmio_read_32(rtc_dev.base + RTC_TSTR); + cal_tamp.dr = mmio_read_32(rtc_dev.base + RTC_TSDR); + stm32_rtc_get_time(&cal_tamp, time); + stm32_rtc_get_date(&cal_tamp, time); +} + +/******************************************************************************* + * This function gets the RTC calendar register values. + * It takes into account the need of reading twice or not, depending on + * frequencies previously setted, and the bypass or not of the shadow + * registers. This service is exposed externally. + ******************************************************************************/ +void stm32_rtc_get_calendar(struct stm32_rtc_calendar *calendar) +{ + bool read_twice = stm32mp1_rtc_get_read_twice(); + + stm32_rtc_regs_lock(); + stm32mp_clk_enable(rtc_dev.clock); + + stm32_rtc_read_calendar(calendar); + + if (read_twice) { + uint32_t tr_save = calendar->tr; + + stm32_rtc_read_calendar(calendar); + + if (calendar->tr != tr_save) { + stm32_rtc_read_calendar(calendar); + } + } + + stm32mp_clk_disable(rtc_dev.clock); + stm32_rtc_regs_unlock(); +} + +/******************************************************************************* + * This function computes the second fraction in milliseconds. + * The returned value is a uint32_t between 0 and 1000. + ******************************************************************************/ +static uint32_t stm32_rtc_get_second_fraction(struct stm32_rtc_calendar *cal) +{ + uint32_t prediv_s = mmio_read_32(rtc_dev.base + RTC_PRER) & + RTC_PRER_PREDIV_S_MASK; + uint32_t ss = cal->ssr & RTC_SSR_SS_MASK; + + return ((prediv_s - ss) * 1000U) / (prediv_s + 1U); +} + +/******************************************************************************* + * This function computes the fraction difference between two timestamps. + * Here again the returned value is in milliseconds. + ******************************************************************************/ +static unsigned long long stm32_rtc_diff_frac(struct stm32_rtc_calendar *cur, + struct stm32_rtc_calendar *ref) +{ + unsigned long long val_r; + unsigned long long val_c; + + val_r = stm32_rtc_get_second_fraction(ref); + val_c = stm32_rtc_get_second_fraction(cur); + + if (val_c >= val_r) { + return val_c - val_r; + } else { + return 1000U - val_r + val_c; + } +} + +/******************************************************************************* + * This function computes the time difference between two timestamps. + * It includes seconds, minutes and hours. + * Here again the returned value is in milliseconds. + ******************************************************************************/ +static unsigned long long stm32_rtc_diff_time(struct stm32_rtc_time *current, + struct stm32_rtc_time *ref) +{ + signed long long diff_in_s; + signed long long curr_s; + signed long long ref_s; + + curr_s = (signed long long)current->sec + + (((signed long long)current->min + + (((signed long long)current->hour * 60))) * 60); + + ref_s = (signed long long)ref->sec + + (((signed long long)ref->min + + (((signed long long)ref->hour * 60))) * 60); + + diff_in_s = curr_s - ref_s; + if (diff_in_s < 0) { + diff_in_s += 24 * 60 * 60; + } + + return (unsigned long long)diff_in_s * 1000U; +} + +/******************************************************************************* + * This function determines if the year is leap or not. + * Returned value is true or false. + ******************************************************************************/ +static bool stm32_is_a_leap_year(uint32_t year) +{ + return ((year % 4U) == 0U) && + (((year % 100U) != 0U) || ((year % 400U) == 0U)); +} + +/******************************************************************************* + * This function computes the date difference between two timestamps. + * It includes days, months, years, with exceptions. + * Here again the returned value is in milliseconds. + ******************************************************************************/ +static unsigned long long stm32_rtc_diff_date(struct stm32_rtc_time *current, + struct stm32_rtc_time *ref) +{ + uint32_t diff_in_days = 0; + uint32_t m; + static const uint8_t month_len[NB_MONTHS] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + /* Get the number of non-entire month days */ + if (current->day >= ref->day) { + diff_in_days += current->day - ref->day; + } else { + diff_in_days += (uint32_t)month_len[ref->month - 1U] - + ref->day + current->day; + } + + /* Get the number of entire months, and compute the related days */ + if (current->month > (ref->month + 1U)) { + for (m = (ref->month + 1U); (m < current->month) && + (m < 12U); m++) { + diff_in_days += (uint32_t)month_len[m - 1U]; + } + } + + if (current->month < (ref->month - 1U)) { + for (m = 1U; (m < current->month) && (m < 12U); m++) { + diff_in_days += (uint32_t)month_len[m - 1U]; + } + + for (m = (ref->month + 1U); m < 12U; m++) { + diff_in_days += (uint32_t)month_len[m - 1U]; + } + } + + /* Get complete years */ + if (current->year > (ref->year + 1U)) { + diff_in_days += (current->year - ref->year - 1U) * 365U; + } + + /* Particular cases: leap years (one day more) */ + if (diff_in_days > 0U) { + if (current->year == ref->year) { + if (stm32_is_a_leap_year(current->year)) { + if ((ref->month <= 2U) && + (current->month >= 3U) && + (current->day <= 28U)) { + diff_in_days++; + } + } + } else { + uint32_t y; + + /* Ref year is leap */ + if ((stm32_is_a_leap_year(ref->year)) && + (ref->month <= 2U) && (ref->day <= 28U)) { + diff_in_days++; + } + + /* Current year is leap */ + if ((stm32_is_a_leap_year(current->year)) && + (current->month >= 3U)) { + diff_in_days++; + } + + /* Interleaved years are leap */ + for (y = ref->year + 1U; y < current->year; y++) { + if (stm32_is_a_leap_year(y)) { + diff_in_days++; + } + } + } + } + + return (24ULL * 60U * 60U * 1000U) * (unsigned long long)diff_in_days; +} + +/******************************************************************************* + * This function computes the date difference between two rtc value. + * Here again the returned value is in milliseconds. + ******************************************************************************/ +unsigned long long stm32_rtc_diff_calendar(struct stm32_rtc_calendar *cur, + struct stm32_rtc_calendar *ref) +{ + unsigned long long diff_in_ms = 0; + struct stm32_rtc_time curr_t; + struct stm32_rtc_time ref_t; + + stm32mp_clk_enable(rtc_dev.clock); + + stm32_rtc_get_date(cur, &curr_t); + stm32_rtc_get_date(ref, &ref_t); + stm32_rtc_get_time(cur, &curr_t); + stm32_rtc_get_time(ref, &ref_t); + + diff_in_ms += stm32_rtc_diff_frac(cur, ref); + diff_in_ms += stm32_rtc_diff_time(&curr_t, &ref_t); + diff_in_ms += stm32_rtc_diff_date(&curr_t, &ref_t); + + stm32mp_clk_disable(rtc_dev.clock); + + return diff_in_ms; +} + +/******************************************************************************* + * This function fill the RTC timestamp structure. + ******************************************************************************/ +void stm32_rtc_get_timestamp(struct stm32_rtc_time *tamp_ts) +{ + stm32_rtc_regs_lock(); + stm32mp_clk_enable(rtc_dev.clock); + + if ((mmio_read_32(rtc_dev.base + RTC_SR) & RTC_SR_TSF) != 0U) { + /* Print timestamp for tamper event */ + stm32_rtc_read_timestamp(tamp_ts); + mmio_setbits_32(rtc_dev.base + RTC_SCR, RTC_SCR_CTSF); + if ((mmio_read_32(rtc_dev.base + RTC_SR) & RTC_SR_TSOVF) != + 0U) { + /* Overflow detected */ + mmio_setbits_32(rtc_dev.base + RTC_SCR, RTC_SCR_CTSOVF); + } + } + + stm32mp_clk_disable(rtc_dev.clock); + stm32_rtc_regs_unlock(); +} + +/******************************************************************************* + * This function enable the timestamp bit for tamper and secure timestamp + * access. + ******************************************************************************/ +void stm32_rtc_set_tamper_timestamp(void) +{ + stm32_rtc_regs_lock(); + stm32mp_clk_enable(rtc_dev.clock); + + stm32_rtc_write_unprotect(); + + /* Enable tamper timestamper */ + mmio_setbits_32(rtc_dev.base + RTC_CR, RTC_CR_TAMPTS); + + /* Secure Timestamp bit */ + mmio_clrbits_32(rtc_dev.base + RTC_SMCR, RTC_SMCR_TS_DPROT); + + stm32_rtc_write_protect(); + + stm32mp_clk_disable(rtc_dev.clock); + stm32_rtc_regs_unlock(); +} + +/******************************************************************************* + * This function return state of tamper timestamp. + ******************************************************************************/ +bool stm32_rtc_is_timestamp_enable(void) +{ + bool ret; + + stm32mp_clk_enable(rtc_dev.clock); + + ret = (mmio_read_32(rtc_dev.base + RTC_CR) & RTC_CR_TAMPTS) != 0U; + + stm32mp_clk_disable(rtc_dev.clock); + + return ret; +} + +/******************************************************************************* + * RTC initialisation function. + ******************************************************************************/ +int stm32_rtc_init(void) +{ + int node; + + node = dt_get_node(&rtc_dev, -1, RTC_COMPAT); + if (node < 0) { + return node; + } + + return 0; +} diff --git a/fdts/stm32mp157-pinctrl.dtsi b/fdts/stm32mp157-pinctrl.dtsi index 8e480b2..4b746b2 100644 --- a/fdts/stm32mp157-pinctrl.dtsi +++ b/fdts/stm32mp157-pinctrl.dtsi @@ -214,6 +214,16 @@ }; }; + i2c2_pins_a: i2c2-0 { + pins { + pinmux = , /* I2C2_SCL */ + ; /* I2C2_SDA */ + bias-disable; + drive-open-drain; + slew-rate = <0>; + }; + }; + sdmmc2_b4_pins_a: sdmmc2-b4-0 { pins1 { pinmux = , /* SDMMC2_D0 */ diff --git a/fdts/stm32mp157a-dk1.dts b/fdts/stm32mp157a-dk1.dts index 4ea83f7..e5b092b 100644 --- a/fdts/stm32mp157a-dk1.dts +++ b/fdts/stm32mp157a-dk1.dts @@ -29,9 +29,9 @@ st,digbypass; }; -&i2c4 { +&i2c2 { pinctrl-names = "default"; - pinctrl-0 = <&i2c4_pins_a>; + pinctrl-0 = <&i2c2_pins_a>; i2c-scl-rising-time-ns = <185>; i2c-scl-falling-time-ns = <20>; status = "okay"; @@ -162,9 +162,9 @@ status = "okay"; }; -&sdmmc1 { +&sdmmc2 { pinctrl-names = "default"; - pinctrl-0 = <&sdmmc1_b4_pins_a>; + pinctrl-0 = <&sdmmc2_b4_pins_a>; broken-cd; st,neg-edge; bus-width = <4>; @@ -192,6 +192,7 @@ /* ATF Specific */ #include +#include #include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi" #include "stm32mp157c-security.dtsi" @@ -244,7 +245,7 @@ CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK - CLK_ETH_DISABLED + CLK_ETH_PLL4P CLK_SDMMC12_PLL4P CLK_DSI_DSIPLL CLK_STGEN_HSE @@ -296,10 +297,15 @@ frac = < 0x1a04 >; }; - /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ + /* + * ETH_CLK required a 125MHz clock, so + * original: VCO = 594.0 MHz => P = 99, Q = 74, R = 74 + * current : VCO = 750.0 MHz => P =125, Q = 75, R = 75 + */ pll4: st,pll@3 { - cfg = < 3 98 5 7 7 PQR(1,1,1) >; + cfg = < 3 124 5 11 11 PQR(1,1,1) >; }; + }; &bsec { @@ -309,3 +315,138 @@ secure-status = "okay"; }; }; + +&pwr { + system_suspend_supported_soc_modes = < + STM32_PM_CSLEEP_RUN + STM32_PM_CSTOP_ALLOW_LP_STOP + STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR + >; + + system_off_soc_mode = ; +}; + +/* Low-power states of regulators */ +&vddcore { + lp-stop { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1200000>; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd_ddr { + lp-stop { + regulator-suspend-microvolt = <1350000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-suspend-microvolt = <1350000>; + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd { + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; +}; + +&v3v3 { + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&v1v8_audio { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&v3v3_hdmi { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vtt_ddr { + lp-stop { + regulator-off-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd_usb { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdda { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&v1v2_hdmi { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vref_ddr { + lp-stop { + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; diff --git a/fdts/stm32mp157c-ed1.dts b/fdts/stm32mp157c-ed1.dts index 7794925..d766cc6 100644 --- a/fdts/stm32mp157c-ed1.dts +++ b/fdts/stm32mp157c-ed1.dts @@ -196,6 +196,7 @@ /* ATF Specific */ #include +#include #include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" #include "stm32mp157c-security.dtsi" @@ -315,3 +316,152 @@ secure-status = "okay"; }; }; + +&pwr { + system_suspend_supported_soc_modes = < + STM32_PM_CSLEEP_RUN + STM32_PM_CSTOP_ALLOW_LP_STOP + STM32_PM_CSTOP_ALLOW_LPLV_STOP + STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR + >; + system_off_soc_mode = ; +}; + +/* Low-power states of regulators */ +&vddcore { + lp-stop { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1200000>; + }; + lplv-stop { + regulator-on-in-suspend; + regulator-suspend-microvolt = <900000>; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd_ddr { + lp-stop { + regulator-suspend-microvolt = <1350000>; + regulator-on-in-suspend; + }; + lplv-stop { + regulator-suspend-microvolt = <1350000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-suspend-microvolt = <1350000>; + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd { + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + lplv-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; +}; + +&v3v3 { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdda { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&v2v8 { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vtt_ddr { + lp-stop { + regulator-off-in-suspend; + }; + lplv-stop { + regulator-off-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd_usb { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd_sd { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&v1v8 { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vref_ddr { + lp-stop { + regulator-on-in-suspend; + }; + lplv-stop { + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; diff --git a/fdts/stm32mp157c.dtsi b/fdts/stm32mp157c.dtsi index 0942a91..faa409f 100644 --- a/fdts/stm32mp157c.dtsi +++ b/fdts/stm32mp157c.dtsi @@ -312,6 +312,20 @@ status = "disabled"; }; + + i2c2: i2c@40013000 { + compatible = "st,stm32f7-i2c"; + reg = <0x40013000 0x400>; + interrupt-names = "event", "error"; + interrupts = , + ; + clocks = <&rcc I2C2_K>; + resets = <&rcc I2C2_R>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + i2c4: i2c@5c002000 { compatible = "st,stm32f7-i2c"; reg = <0x5c002000 0x400>; diff --git a/include/arch/aarch32/arch.h b/include/arch/aarch32/arch.h index 2017548..edf1287 100644 --- a/include/arch/aarch32/arch.h +++ b/include/arch/aarch32/arch.h @@ -457,13 +457,13 @@ * system level implementation of the Generic Timer. ******************************************************************************/ /* Physical Count register. */ -#define CNTPCT_LO U(0x0) +#define CNTBASEN_CNTPCT_LO U(0x0) /* Counter Frequency register. */ #define CNTBASEN_CNTFRQ U(0x10) /* Physical Timer CompareValue register. */ -#define CNTP_CVAL_LO U(0x20) +#define CNTBASEN_CNTP_CVAL_LO U(0x20) /* Physical Timer Control register. */ -#define CNTP_CTL U(0x2c) +#define CNTBASEN_CNTP_CTL U(0x2c) /* Physical timer control register bit fields shifts and masks */ #define CNTP_CTL_ENABLE_SHIFT 0 @@ -522,6 +522,9 @@ #define HSTR p15, 4, c1, c1, 3 #define CNTHCTL p15, 4, c14, c1, 0 #define CNTKCTL p15, 0, c14, c1, 0 +#define CNTP_TVAL p15, 0, c14, c2, 0 +#define CNTP_CTL p15, 0, c14, c2, 1 +#define CNTV_CTL p15, 0, c14, c3, 1 #define VPIDR p15, 4, c0, c0, 0 #define VMPIDR p15, 4, c0, c0, 5 #define ISR p15, 0, c12, c1, 0 diff --git a/include/arch/aarch32/arch_helpers.h b/include/arch/aarch32/arch_helpers.h index cbac84b..b060f85 100644 --- a/include/arch/aarch32/arch_helpers.h +++ b/include/arch/aarch32/arch_helpers.h @@ -246,6 +246,9 @@ DEFINE_COPROCR_RW_FUNCS_64(cntvoff, CNTVOFF_64) DEFINE_COPROCR_RW_FUNCS(csselr, CSSELR) DEFINE_COPROCR_RW_FUNCS(hstr, HSTR) +DEFINE_COPROCR_RW_FUNCS(cntp_tval, CNTP_TVAL) +DEFINE_COPROCR_RW_FUNCS(cntp_ctl, CNTP_CTL) +DEFINE_COPROCR_RW_FUNCS(cntv_ctl, CNTV_CTL) DEFINE_COPROCR_RW_FUNCS(cnthp_ctl_el2, CNTHP_CTL) DEFINE_COPROCR_RW_FUNCS(cnthp_tval_el2, CNTHP_TVAL) DEFINE_COPROCR_RW_FUNCS_64(cnthp_cval_el2, CNTHP_CVAL_64) diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h index 3ff2912..307dad5 100644 --- a/include/arch/aarch64/arch.h +++ b/include/arch/aarch64/arch.h @@ -670,13 +670,13 @@ * system level implementation of the Generic Timer. ******************************************************************************/ /* Physical Count register. */ -#define CNTPCT_LO U(0x0) +#define CNTBASEN_CNTPCT_LO U(0x0) /* Counter Frequency register. */ #define CNTBASEN_CNTFRQ U(0x10) /* Physical Timer CompareValue register. */ -#define CNTP_CVAL_LO U(0x20) +#define CNTBASEN_CNTP_CVAL_LO U(0x20) /* Physical Timer Control register. */ -#define CNTP_CTL U(0x2c) +#define CNTBASEN_CNTP_CTL U(0x2c) /* PMCR_EL0 definitions */ #define PMCR_EL0_RESET_VAL U(0x0) diff --git a/include/drivers/st/stm32_rtc.h b/include/drivers/st/stm32_rtc.h new file mode 100644 index 0000000..128dd2d --- /dev/null +++ b/include/drivers/st/stm32_rtc.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_RTC_H +#define STM32_RTC_H + +#include + +#define RTC_TR 0x00U +#define RTC_DR 0x04U +#define RTC_SSR 0x08U +#define RTC_ICSR 0x0CU +#define RTC_PRER 0x10U +#define RTC_WUTR 0x14U +#define RTC_CR 0x18U +#define RTC_SMCR 0x20U +#define RTC_WPR 0x24U +#define RTC_CALR 0x28U +#define RTC_SHIFTR 0x2CU +#define RTC_TSTR 0x30U +#define RTC_TSDR 0x34U +#define RTC_TSSSR 0x38U +#define RTC_ALRMAR 0x40U +#define RTC_ALRMASSR 0x44U +#define RTC_ALRMBR 0x48U +#define RTC_ALRMBSSR 0x4CU +#define RTC_SR 0x50U +#define RTC_SCR 0x5CU +#define RTC_OR 0x60U + +struct stm32_rtc_calendar { + uint32_t ssr; + uint32_t tr; + uint32_t dr; +}; + +enum months { + JANUARY = 1, + FEBRUARY, + MARCH, + APRIL, + MAY, + JUNE, + JULY, + AUGUST, + SEPTEMBER, + OCTOBER, + NOVEMBER, + DECEMBER, + NB_MONTHS = 12 +}; + +struct stm32_rtc_time { + uint32_t hour; + uint32_t min; + uint32_t sec; + uint32_t wday; + uint32_t day; + enum months month; + uint32_t year; +}; + +void stm32_rtc_get_calendar(struct stm32_rtc_calendar *calendar); +unsigned long long stm32_rtc_diff_calendar(struct stm32_rtc_calendar *current, + struct stm32_rtc_calendar *ref); +void stm32_rtc_set_tamper_timestamp(void); +bool stm32_rtc_is_timestamp_enable(void); +void stm32_rtc_get_timestamp(struct stm32_rtc_time *tamp_ts); +int stm32_rtc_init(void); + +/* SMP protection on RTC registers access */ +void stm32_rtc_regs_lock(void); +void stm32_rtc_regs_unlock(void); + +#endif /* STM32_RTC_H */ diff --git a/include/drivers/st/stm32mp1_clk.h b/include/drivers/st/stm32mp1_clk.h index 1ebd39f..81ae5d8 100644 --- a/include/drivers/st/stm32mp1_clk.h +++ b/include/drivers/st/stm32mp1_clk.h @@ -53,6 +53,8 @@ unsigned int stm32mp1_clk_get_refcount(unsigned long id); +bool stm32mp1_rtc_get_read_twice(void); + /* SMP protection on RCC registers access */ void stm32mp1_clk_rcc_regs_lock(void); void stm32mp1_clk_rcc_regs_unlock(void); diff --git a/include/drivers/st/stm32mp1_ddr.h b/include/drivers/st/stm32mp1_ddr.h index 4ab37d6..f52609f 100644 --- a/include/drivers/st/stm32mp1_ddr.h +++ b/include/drivers/st/stm32mp1_ddr.h @@ -8,9 +8,6 @@ #define STM32MP1_DDR_H #include -#include - -#define DT_DDR_COMPAT "st,stm32mp1-ddr" struct stm32mp1_ddr_size { uint64_t base; @@ -166,9 +163,12 @@ struct stm32mp1_ddrphy_reg p_reg; struct stm32mp1_ddrphy_timing p_timing; struct stm32mp1_ddrphy_cal p_cal; + bool self_refresh; + uint32_t zdata; }; int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed); void stm32mp1_ddr_init(struct ddr_info *priv, struct stm32mp1_ddr_config *config); + #endif /* STM32MP1_DDR_H */ diff --git a/include/drivers/st/stm32mp1_ddr_helpers.h b/include/drivers/st/stm32mp1_ddr_helpers.h index 38f2415..80bf9de 100644 --- a/include/drivers/st/stm32mp1_ddr_helpers.h +++ b/include/drivers/st/stm32mp1_ddr_helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,13 @@ #ifndef STM32MP1_DDR_HELPERS_H #define STM32MP1_DDR_HELPERS_H +#include + void ddr_enable_clock(void); +int ddr_sw_self_refresh_exit(void); +int ddr_standby_sr_entry(uint32_t *zq0cr0_zdata); +void ddr_sr_mode_ssr(void); +void ddr_sr_mode_asr(void); +void ddr_sr_mode_hsr(void); #endif /* STM32MP1_DDR_HELPERS_H */ diff --git a/include/drivers/st/stm32mp1_pwr.h b/include/drivers/st/stm32mp1_pwr.h index e17df44..9b662f2 100644 --- a/include/drivers/st/stm32mp1_pwr.h +++ b/include/drivers/st/stm32mp1_pwr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,20 +13,39 @@ #define PWR_CR2 U(0x08) #define PWR_CR3 U(0x0C) #define PWR_MPUCR U(0x10) +#define PWR_MCUCR U(0x14) #define PWR_WKUPCR U(0x20) #define PWR_MPUWKUPENR U(0x28) +#define PWR_OFFSET_MASK GENMASK(9, 0) + #define PWR_CR1_LPDS BIT(0) #define PWR_CR1_LPCFG BIT(1) #define PWR_CR1_LVDS BIT(2) #define PWR_CR1_DBP BIT(8) +#define PWR_CR2_BREN BIT(0) +#define PWR_CR2_RREN BIT(1) +#define PWR_CR2_BRRDY BIT(16) +#define PWR_CR2_RRRDY BIT(17) + +#define PWR_CR3_VBE BIT(8) +#define PWR_CR3_VBRS BIT(9) #define PWR_CR3_DDRSREN BIT(10) #define PWR_CR3_DDRSRDIS BIT(11) #define PWR_CR3_DDRRETEN BIT(12) +#define PWR_CR3_USB33DEN BIT(24) +#define PWR_CR3_REG18EN BIT(28) +#define PWR_CR3_REG11EN BIT(30) #define PWR_MPUCR_PDDS BIT(0) #define PWR_MPUCR_CSTDBYDIS BIT(3) #define PWR_MPUCR_CSSF BIT(9) +#define PWR_MCUCR_PDDS BIT(0) + +#define PWR_WKUPCR_MASK GENMASK(27, 16) | GENMASK(13, 8) | GENMASK(5, 0) + +#define PWR_MPUWKUPENR_MASK GENMASK(5, 0) + #endif /* STM32MP1_PWR_H */ diff --git a/include/drivers/st/stm32mp_pmic.h b/include/drivers/st/stm32mp_pmic.h index 984cd60..f5810f3 100644 --- a/include/drivers/st/stm32mp_pmic.h +++ b/include/drivers/st/stm32mp_pmic.h @@ -27,6 +27,8 @@ */ int dt_pmic_configure_boot_on_regulators(void); +int dt_pmic_set_lp_config(const char *node_name); + /* * initialize_pmic_i2c - Initialize I2C for the PMIC control * diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h index f7e293b..e4ceea5 100644 --- a/include/drivers/st/stpmic1.h +++ b/include/drivers/st/stpmic1.h @@ -161,6 +161,10 @@ int stpmic1_regulator_voltage_get(const char *name); int stpmic1_regulator_pull_down_set(const char *name); int stpmic1_regulator_mask_reset_set(const char *name); +int stpmic1_lp_copy_reg(const char *name); +int stpmic1_lp_reg_on_off(const char *name, uint8_t enable); +int stpmic1_lp_set_mode(const char *name, uint8_t hplp); +int stpmic1_lp_set_voltage(const char *name, uint16_t millivolts); void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr); int stpmic1_get_version(unsigned long *version); diff --git a/include/dt-bindings/power/stm32mp1-power.h b/include/dt-bindings/power/stm32mp1-power.h new file mode 100644 index 0000000..d588dd7 --- /dev/null +++ b/include/dt-bindings/power/stm32mp1-power.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ +/* + * Copyright (C) 2018-2019, STMicroelectronics - All Rights Reserved + * Author: Yann Gautier for STMicroelectronics. + */ + +#ifndef DT_BINDINGS_STM32MP1_POWER_H +#define DT_BINDINGS_STM32MP1_POWER_H + +#define STM32_PM_CSLEEP_RUN 0 +#define STM32_PM_CSTOP_ALLOW_STOP 1 +#define STM32_PM_CSTOP_ALLOW_LP_STOP 2 +#define STM32_PM_CSTOP_ALLOW_LPLV_STOP 3 +#define STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR 4 +#define STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF 5 +#define STM32_PM_SHUTDOWN 6 +#define STM32_PM_MAX_SOC_MODE 7 + +#endif /* DT_BINDINGS_STM32MP1_POWER_H */ diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h index 4f85679..0524028 100644 --- a/plat/st/common/include/stm32mp_common.h +++ b/plat/st/common/include/stm32mp_common.h @@ -33,6 +33,10 @@ /* Check MMU status to allow spinlock use */ bool stm32mp_lock_available(void); +/* SMP protection on PWR registers access */ +void stm32mp_pwr_regs_lock(void); +void stm32mp_pwr_regs_unlock(void); + /* Get IWDG platform instance ID from peripheral IO memory base address */ uint32_t stm32_iwdg_get_instance(uintptr_t base); diff --git a/plat/st/common/stm32mp_common.c b/plat/st/common/stm32mp_common.c index afa87f4..5e7c74b 100644 --- a/plat/st/common/stm32mp_common.c +++ b/plat/st/common/stm32mp_common.c @@ -12,8 +12,11 @@ #include #include #include +#include #include +static struct spinlock lock; + uintptr_t plat_get_ns_image_entrypoint(void) { return BL33_BASE; @@ -96,6 +99,20 @@ return (read_sctlr() & c_m_bits) == c_m_bits; } +void stm32mp_pwr_regs_lock(void) +{ + if (stm32mp_lock_available()) { + spin_lock(&lock); + } +} + +void stm32mp_pwr_regs_unlock(void) +{ + if (stm32mp_lock_available()) { + spin_unlock(&lock); + } +} + uintptr_t stm32_get_gpio_bank_base(unsigned int bank) { if (bank == GPIO_BANK_Z) { diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c index 17da490..863c2cc 100644 --- a/plat/st/common/stm32mp_dt.c +++ b/plat/st/common/stm32mp_dt.c @@ -13,8 +13,6 @@ #include #include -#include -#include #include diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index d9e29b4..c729c02 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -31,6 +31,8 @@ #include #include +#define PWRLP_TEMPO_5_HSI 5 + static struct console_stm32 console; static struct stm32mp_auth_ops stm32mp1_auth_ops; @@ -160,6 +162,10 @@ uint32_t clk_rate; uintptr_t pwr_base; uintptr_t rcc_base; + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, @@ -204,6 +210,11 @@ pwr_base = stm32mp_pwr_base(); rcc_base = stm32mp_rcc_base(); + /* Clear Stop Request bits to correctly manage low-power exit */ + mmio_write_32(rcc_base + RCC_MP_SREQCLRR, + (uint32_t)(RCC_MP_SREQCLRR_STPREQ_P0 | + RCC_MP_SREQCLRR_STPREQ_P1)); + /* * Disable the backup domain write protection. * The protection is enable at each reset by hardware @@ -215,6 +226,12 @@ ; } + /* + * Configure Standby mode available for MCU by default + * and allow to switch in standby SoC in all case + */ + mmio_setbits_32(pwr_base + PWR_MCUCR, PWR_MCUCR_PDDS); + if (bsec_probe() != 0) { panic(); } @@ -231,9 +248,25 @@ mmio_clrbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); } + /* Wait 5 HSI periods before re-enabling PLLs after STOP modes */ + mmio_clrsetbits_32(rcc_base + RCC_PWRLPDLYCR, + RCC_PWRLPDLYCR_PWRLP_DLY_MASK, + PWRLP_TEMPO_5_HSI); + + /* Disable retention and backup RAM content after standby */ + mmio_clrbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | PWR_CR2_RREN); + /* Disable MCKPROT */ mmio_clrbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); + if ((boot_context->boot_action != + BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY) && + (boot_context->boot_action != + BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY)) { + mmio_write_32(bkpr_core1_addr, 0); + mmio_write_32(bkpr_core1_magic, 0); + } + generic_delay_timer_init(); if (stm32mp1_clk_probe() < 0) { diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h index 2284970..6088b37 100644 --- a/plat/st/stm32mp1/include/boot_api.h +++ b/plat/st/stm32mp1/include/boot_api.h @@ -11,6 +11,90 @@ #include /* + * Exported constants + */ + +/* + * Boot Context related definitions + */ + +/* + * Possible value of boot context field 'boot_action' + */ +/* Boot action is Process Cold Boot */ +#define BOOT_API_CTX_BOOT_ACTION_COLD_BOOT_PROCESS 0x09U +/* Boot action is Process Wakeup from CSTANDBY */ +#define BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY 0x0AU +/* Boot action is Process Wakeup from STANDBY */ +#define BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY 0x0BU +/* Boot action is Process Engineering Boot */ +#define BOOT_API_CTX_BOOT_ACTION_ENGI_BOOT 0x0CU + +#define BOOT_API_CTX_BOOT_ACTION_MPU_CORE0_RESET_PROCESS 0x0F + +/* + * Possible value of boot context field 'stby_exit_status' + */ + +/* The boot reason is not a STANDBY Exit reason */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_NO_STANDBY 0x00 + +/* STANDBY Exit with MPU_BEN=1, MCU_BEN=0 */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MPU_ONLY 0x01 + +/* + * STANDBY Exit with MPU_BEN=1, MCU_BEN=1, MPU will go for cold boot + * MCU restarted by bootROM + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_ALL_CORES 0x02 + +/* + * STANDBY Exit with MPU_BEN=1, MCU_BEN=1, MPU will go for cold boot + * but MCU restart aborted (code integrity check) : have not been restarted + * by bootROM + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_ALL_CORES_MCU_ABT 0x03 + +/* + * STANDBY Exit with MPU_BEN=0, MCU_BEN=1, MPU gone to CSTANDBY, + * MCU restarted correctly by bootROM + * This value should never be read by FSBL, because not executed in that case + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MCU_ONLY 0x04 + +/* + * STANDBY Exit with MPU_BEN=0, MCU_BEN=1, MCU restart aborted + * due code integrity check, then MPU will go for cold boot despite + * was not planned initially + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MCU_ONLY_MCU_ABT 0x05 + +/* + * STANDBY Exit with MPU_BEN=1, MCU_BEN=1, MCU restart aborted + * due to MCU security perimeter issue + */ +#define \ +BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_ALL_CORES_MCU_ABT_SEC_PERIMETER_ISSUE 0x06 + +/* + * STANDBY Exit with MPU_BEN=0, MCU_BEN=1, MCU restart aborted + * due to MCU security perimeter issue, then MPU will go for cold boot + * despite was not planned initially + */ +#define \ +BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MCU_ONLY_MCU_ABT_SEC_PERIMETER_ISSUE 0x07 + +/* + * Possible value of boot context field 'cstby_exit_status' + */ +/* The boot reason is not a CSTANDBY Exit reason */ +#define BOOT_API_CTX_CSTBY_EXIT_STATUS_NO_CSTBY 0x00 +/* CSTANDBY Exit with MCU detected as Not running */ +#define BOOT_API_CTX_CSTBY_EXIT_STATUS_MCU_NOT_RUNNING 0x01 +/* CSTANDBY Exit with MCU detected as Running */ +#define BOOT_API_CTX_CSTBY_EXIT_STATUS_MCU_RUNNING 0x02 + +/* * Possible value of boot context field 'auth_status' */ /* No authentication done */ @@ -144,7 +228,26 @@ uint16_t boot_interface_instance; uint32_t reserved1[13]; uint32_t otp_afmux_values[3]; - uint32_t reserved[5]; + uint32_t reserved[2]; + /* + * Log to boot context, what was the kind of boot action + * takes values from defines BOOT_API_BOOT_ACTION_XXX above + */ + uint32_t boot_action; + /* + * STANDBY Exit status to be checked by FSBL in case + * field 'boot_action' == BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY + * take values from defines above 'BOOT_API_CTX_STBY_EXIT_STATUS_XXX' + * depending on encountered situation + */ + uint32_t stby_exit_status; + /* + * CSTANDBY Exit status to be checked by FSBL in case + * boot_action == BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY + * take values from defines above 'BOOT_API_CTX_CSTBY_EXIT_STATUS_XXX' + * depending on encountered situation + */ + uint32_t cstby_exit_status; uint32_t auth_status; /* diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h index 263e6d6..2f6773e 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -112,6 +112,8 @@ */ #define ARM_IRQ_SEC_PHY_TIMER U(29) +#define ARM_IRQ_NON_SEC_SGI_0 U(0) + #define ARM_IRQ_SEC_SGI_0 U(8) #define ARM_IRQ_SEC_SGI_1 U(9) #define ARM_IRQ_SEC_SGI_2 U(10) diff --git a/plat/st/stm32mp1/include/stm32mp1_context.h b/plat/st/stm32mp1/include/stm32mp1_context.h index 698415a..0816917 100644 --- a/plat/st/stm32mp1/include/stm32mp1_context.h +++ b/plat/st/stm32mp1/include/stm32mp1_context.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,8 +7,19 @@ #ifndef STM32MP1_CONTEXT_H #define STM32MP1_CONTEXT_H +#include #include +#define DDR_CRC_GRANULE 32 + +void stm32_clean_context(void); +int stm32_save_context(uint32_t zq0cr0_zdata); +int stm32_restore_context(void); +int stm32_restore_backup_reg(void); +uint32_t stm32_get_zdata_from_context(void); int stm32_save_boot_interface(uint32_t interface, uint32_t instance); +void stm32_save_ddr_training_area(void); +void stm32_restore_ddr_training_area(void); +uint32_t stm32_pm_get_optee_ep(void); #endif /* STM32MP1_CONTEXT_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_low_power.h b/plat/st/stm32mp1/include/stm32mp1_low_power.h new file mode 100644 index 0000000..82b3d36 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_low_power.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_LOW_POWER_H +#define STM32MP1_LOW_POWER_H + +#include +#include + +void stm32_rcc_wakeup_update(bool state); +void stm32_apply_pmic_suspend_config(uint32_t mode); +void stm32_exit_cstop(void); +void stm32_pwr_down_wfi(void); +void stm32_enter_low_power(uint32_t mode, uint32_t nsec_addr); + +#endif /* STM32MP1_LOW_POWER_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_power_config.h b/plat/st/stm32mp1/include/stm32mp1_power_config.h new file mode 100644 index 0000000..7c7571a --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_power_config.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_POWER_CONFIG_H +#define STM32MP1_POWER_CONFIG_H + +#include +#include + +#define PSCI_MODE_SYSTEM_SUSPEND 0 +#define PSCI_MODE_SYSTEM_OFF 1 + +void stm32mp1_init_lp_states(void); +uint32_t stm32mp1_get_lp_soc_mode(uint32_t psci_mode); + +#endif /* STM32MP1_POWER_CONFIG_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h index e38fca0..7adc7f6 100644 --- a/plat/st/stm32mp1/include/stm32mp1_private.h +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -11,6 +11,9 @@ void configure_mmu(void); +void stm32mp_mask_timer(void); +void __dead2 stm32mp_wait_cpu_reset(void); + void stm32mp1_arch_security_setup(void); void stm32mp1_security_setup(void); diff --git a/plat/st/stm32mp1/plat_image_load.c b/plat/st/stm32mp1/plat_image_load.c index a52db6c..1fb3ec5 100644 --- a/plat/st/stm32mp1/plat_image_load.c +++ b/plat/st/stm32mp1/plat_image_load.c @@ -1,10 +1,14 @@ /* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include + +#include #include +#include #include /******************************************************************************* @@ -16,11 +20,68 @@ flush_bl_params_desc(); } +#ifdef AARCH32_SP_OPTEE +static bool addr_inside_backupsram(uintptr_t addr) +{ + return (addr >= STM32MP_BACKUP_RAM_BASE) && + (addr < (STM32MP_BACKUP_RAM_BASE + STM32MP_BACKUP_RAM_SIZE)); +} +#endif + /******************************************************************************* * This function returns the list of loadable images. ******************************************************************************/ bl_load_info_t *plat_get_bl_image_load_info(void) { + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); +#ifdef AARCH32_SP_OPTEE + bl_mem_params_node_t *bl32 = get_bl_mem_params_node(BL32_IMAGE_ID); +#endif + bl_mem_params_node_t *bl33 = get_bl_mem_params_node(BL33_IMAGE_ID); + uint32_t rstsr = mmio_read_32(stm32mp_rcc_base() + RCC_MP_RSTSCLRR); + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uintptr_t pwr_base = stm32mp_pwr_base(); + + /* + * If going back from CSTANDBY / STANDBY and DDR was in Self-Refresh, + * BL33 must not be loaded as it would overwrite the code already + * in DDR. For this, the BL33 part of the bl_mem_params_desc_ptr + * struct should be modified to skip its loading + */ + if (((boot_context->boot_action == + BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY) || + (boot_context->boot_action == + BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY)) && + ((mmio_read_32(pwr_base + PWR_CR3) & PWR_CR3_DDRSREN) != 0U) && + ((rstsr & RCC_MP_RSTSCLRR_PADRSTF) == 0U)) { + stm32mp_clk_enable(RTCAPB); + + if (mmio_read_32(bkpr_core1_addr) != 0U) { + bl33->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING; + +#ifdef AARCH32_SP_OPTEE + bl32->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING; + bl32->ep_info.pc = stm32_pm_get_optee_ep(); + + if (addr_inside_backupsram(bl32->ep_info.pc)) { + stm32mp_clk_enable(BKPSRAM); + } +#else + /* + * Set ep_info PC to 0, to inform BL32 it is a reset + * after STANDBY + */ + bl33->ep_info.pc = 0; +#endif + } + + stm32mp_clk_disable(RTCAPB); + } + + bl33->image_info.image_max_size = dt_get_ddr_size(); + return get_bl_load_info_from_mem_params_desc(); } diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 90b3e3c..0c57c85 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -94,6 +94,10 @@ BL2_SOURCES += lib/optee/optee_utils.c endif + +# Do not use neon in TF-A code, it leads to issues in low-power functions +TF_CFLAGS += -mfloat-abi=soft + # Macros and rules to build TF binary STM32_TF_ELF_LDFLAGS := --hash-style=gnu --as-needed STM32_DT_BASENAME := $(DTB_FILE_NAME:.dtb=) diff --git a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk index 4188cc5..34530f1 100644 --- a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk +++ b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk @@ -6,9 +6,14 @@ SP_MIN_WITH_SECURE_FIQ := 1 +BL32_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 + BL32_SOURCES += plat/common/aarch32/platform_mp_stack.S \ + drivers/st/rtc/stm32_rtc.c \ plat/st/stm32mp1/sp_min/sp_min_setup.c \ + plat/st/stm32mp1/stm32mp1_low_power.c \ plat/st/stm32mp1/stm32mp1_pm.c \ + plat/st/stm32mp1/stm32mp1_power_config.c \ plat/st/stm32mp1/stm32mp1_topology.c # Generic GIC v2 BL32_SOURCES += drivers/arm/gic/common/gic_common.c \ diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c index e10dfbf..62b358c 100644 --- a/plat/st/stm32mp1/sp_min/sp_min_setup.c +++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +30,7 @@ #include #include +#include /****************************************************************************** * Placeholder variables for copying the arguments that have been passed to @@ -37,6 +40,27 @@ static struct console_stm32 console; +static void stm32_sgi1_it_handler(void) +{ + uint32_t id; + + stm32mp_mask_timer(); + + gicv2_end_of_interrupt(ARM_IRQ_SEC_SGI_1); + + do { + id = plat_ic_get_pending_interrupt_id(); + + if (id <= MAX_SPI_ID) { + gicv2_end_of_interrupt(id); + + plat_ic_disable_interrupt(id); + } + } while (id <= MAX_SPI_ID); + + stm32mp_wait_cpu_reset(); +} + /******************************************************************************* * Interrupt handler for FIQ (secure IRQ) ******************************************************************************/ @@ -47,12 +71,15 @@ ERROR("STM32MP1_IRQ_TZC400 generated\n"); panic(); break; + case ARM_IRQ_SEC_SGI_1: + stm32_sgi1_it_handler(); + break; case STM32MP1_IRQ_AXIERRIRQ: ERROR("STM32MP1_IRQ_AXIERRIRQ generated\n"); panic(); break; default: - ERROR("SECURE IT handler not define for it : %u", id); + ERROR("SECURE IT handler not define for it : %u\n", id); break; } } @@ -66,11 +93,54 @@ entry_point_info_t *sp_min_plat_get_bl33_ep_info(void) { entry_point_info_t *next_image_info; + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); next_image_info = &bl33_image_ep_info; + /* + * PC is set to 0 when resetting after STANDBY + * The context should be restored, and the image information + * should be filled with what what was saved + */ if (next_image_info->pc == 0U) { - return NULL; + void *cpu_context; + uint32_t magic_nb, saved_pc; + + stm32mp_clk_enable(RTCAPB); + + magic_nb = mmio_read_32(bkpr_core1_magic); + saved_pc = mmio_read_32(bkpr_core1_addr); + + stm32mp_clk_disable(RTCAPB); + + if (stm32_restore_context() != 0) { + panic(); + } + + cpu_context = cm_get_context(NON_SECURE); + + next_image_info->spsr = read_ctx_reg(get_regs_ctx(cpu_context), + CTX_SPSR); + + /* PC should be retrieved in backup register if OK, else it can + * be retrieved from non-secure context + */ + if (magic_nb == BOOT_API_A7_CORE0_MAGIC_NUMBER) { + /* BL33 return address should be in DDR */ + if ((saved_pc < STM32MP_DDR_BASE) || + (saved_pc > (STM32MP_DDR_BASE + + (dt_get_ddr_size() - 1U)))) { + panic(); + } + + next_image_info->pc = saved_pc; + } else { + next_image_info->pc = + read_ctx_reg(get_regs_ctx(cpu_context), CTX_LR); + } } return next_image_info; @@ -144,6 +214,36 @@ #endif console_set_scope(&console.console, console_flags); } + + if (dt_pmic_status() > 0) { + initialize_pmic(); + } + + stm32mp1_init_lp_states(); +} + +/******************************************************************************* + * Set security setup in sp_min + ******************************************************************************/ +static void stm32mp1_sp_min_security_setup(void) +{ + int ret; + + /* Unlock ETZPC securable peripherals */ +#define STM32MP1_ETZPC_BASE 0x5C007000U +#define ETZPC_DECPROT0 0x010U + mmio_write_32(STM32MP1_ETZPC_BASE + ETZPC_DECPROT0, 0xFFFFFFFF); + + /* Set GPIO bank Z as non secure */ + for (uint32_t pin = 0U; pin < STM32MP_GPIOZ_PIN_MAX_COUNT; pin++) { + set_gpio_secure_cfg(GPIO_BANK_Z, pin, false); + } + + /* Init rtc driver */ + ret = stm32_rtc_init(); + if (ret < 0) { + WARN("RTC driver init error %i\n", ret); + } } /******************************************************************************* @@ -158,15 +258,8 @@ stm32mp1_gic_init(); - /* Unlock ETZPC securable peripherals */ -#define STM32MP1_ETZPC_BASE 0x5C007000U -#define ETZPC_DECPROT0 0x010U - mmio_write_32(STM32MP1_ETZPC_BASE + ETZPC_DECPROT0, 0xFFFFFFFF); - - /* Set GPIO bank Z as non secure */ - for (uint32_t pin = 0U; pin < STM32MP_GPIOZ_PIN_MAX_COUNT; pin++) { - set_gpio_secure_cfg(GPIO_BANK_Z, pin, false); - } + /* Update security settings */ + stm32mp1_sp_min_security_setup(); if (stm32_iwdg_init() < 0) { panic(); diff --git a/plat/st/stm32mp1/stm32mp1_context.c b/plat/st/stm32mp1/stm32mp1_context.c index cf8a91e..ed54fc6 100644 --- a/plat/st/stm32mp1/stm32mp1_context.c +++ b/plat/st/stm32mp1/stm32mp1_context.c @@ -5,19 +5,171 @@ */ #include +#include #include +#include +#include +#include #include +#include #include +#include #include +#include +#include +#include #include +#include + #define TAMP_BOOT_ITF_BACKUP_REG_ID U(20) #define TAMP_BOOT_ITF_MASK U(0x0000FF00) #define TAMP_BOOT_ITF_SHIFT 8 +#define TRAINING_AREA_SIZE 64 + +#ifdef AARCH32_SP_OPTEE +/* OPTEE_MAILBOX_MAGIC relates to struct backup_data_s as defined */ +#define OPTEE_MAILBOX_MAGIC_V1 0x01 +#define OPTEE_MAILBOX_MAGIC ((OPTEE_MAILBOX_MAGIC_V1 << 16) + \ + TRAINING_AREA_SIZE) +#endif + +struct backup_data_s { +#ifdef AARCH32_SP_OPTEE + uint32_t magic; + uint32_t core0_resume_hint; + uint32_t zq0cr0_zdata; + uint8_t ddr_training_backup[TRAINING_AREA_SIZE]; +#else + smc_ctx_t saved_smc_context[PLATFORM_CORE_COUNT]; + cpu_context_t saved_cpu_context[PLATFORM_CORE_COUNT]; + uint32_t zq0cr0_zdata; + struct stm32_rtc_calendar rtc; + uint8_t ddr_training_backup[TRAINING_AREA_SIZE]; +#endif +}; + +#ifdef AARCH32_SP_OPTEE +uint32_t stm32_pm_get_optee_ep(void) +{ + struct backup_data_s *backup_data; + uint32_t ep; + + stm32mp_clk_enable(BKPSRAM); + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + if (backup_data->magic != OPTEE_MAILBOX_MAGIC) { + panic(); + } + + ep = backup_data->core0_resume_hint; + + stm32mp_clk_disable(BKPSRAM); + + return ep; +} +#else /*AARCH32_SP_OPTEE*/ +void stm32_clean_context(void) +{ + stm32mp_clk_enable(BKPSRAM); + + zeromem((void *)STM32MP_BACKUP_RAM_BASE, sizeof(struct backup_data_s)); + + stm32mp_clk_disable(BKPSRAM); +} + +int stm32_save_context(uint32_t zq0cr0_zdata) +{ + void *smc_context; + void *cpu_context; + struct backup_data_s *backup_data; + + stm32mp_clk_enable(BKPSRAM); + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + /* Retrieve smc context struct address */ + smc_context = smc_get_ctx(NON_SECURE); + + /* Retrieve smc context struct address */ + cpu_context = cm_get_context(NON_SECURE); + + /* Save context in Backup SRAM */ + memcpy(&backup_data->saved_smc_context[0], smc_context, + sizeof(smc_ctx_t) * PLATFORM_CORE_COUNT); + memcpy(&backup_data->saved_cpu_context[0], cpu_context, + sizeof(cpu_context_t) * PLATFORM_CORE_COUNT); + + backup_data->zq0cr0_zdata = zq0cr0_zdata; + + stm32_rtc_get_calendar(&backup_data->rtc); + + stm32mp_clk_disable(BKPSRAM); + + return 0; +} + +int stm32_restore_context(void) +{ + void *smc_context; + void *cpu_context; + struct backup_data_s *backup_data; + struct stm32_rtc_calendar current_calendar; + unsigned long long stdby_time_in_ms; + + stm32mp_clk_enable(BKPSRAM); + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + /* Retrieve smc context struct address */ + smc_context = smc_get_ctx(NON_SECURE); + + /* Retrieve smc context struct address */ + cpu_context = cm_get_context(NON_SECURE); + + /* Restore data from Backup SRAM */ + memcpy(smc_context, backup_data->saved_smc_context, + sizeof(smc_ctx_t) * PLATFORM_CORE_COUNT); + memcpy(cpu_context, backup_data->saved_cpu_context, + sizeof(cpu_context_t) * PLATFORM_CORE_COUNT); + + /* update STGEN counter with standby mode length */ + stm32_rtc_get_calendar(¤t_calendar); + stdby_time_in_ms = stm32_rtc_diff_calendar(¤t_calendar, + &backup_data->rtc); + stm32mp1_stgen_increment(stdby_time_in_ms); + + stm32mp_clk_disable(BKPSRAM); + + return 0; +} +#endif /*AARCH32_SP_OPTEE*/ + +uint32_t stm32_get_zdata_from_context(void) +{ + struct backup_data_s *backup_data; + uint32_t zdata; + + stm32mp_clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + zdata = (backup_data->zq0cr0_zdata >> DDRPHYC_ZQ0CRN_ZDATA_SHIFT) & + DDRPHYC_ZQ0CRN_ZDATA_MASK; + + stm32mp_clk_disable(BKPSRAM); + + return zdata; +} + int stm32_save_boot_interface(uint32_t interface, uint32_t instance) { uint32_t bkpr_itf_idx = tamp_bkpr(TAMP_BOOT_ITF_BACKUP_REG_ID); @@ -33,3 +185,50 @@ return 0; } + +#if defined(IMAGE_BL32) +/* + * When returning from STANDBY, the 64 first bytes of DDR will be overwritten + * during DDR DQS training. This area must then be saved before going to + * standby, and will be restored after + */ +void stm32_save_ddr_training_area(void) +{ + struct backup_data_s *backup_data; + int ret __unused; + + stm32mp_clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + PAGE_SIZE, MT_MEMORY | MT_RW | MT_NS); + assert(ret == 0); + + memcpy(&backup_data->ddr_training_backup, + (const uint32_t *)STM32MP_DDR_BASE, + TRAINING_AREA_SIZE); + dsb(); + + ret = mmap_remove_dynamic_region(STM32MP_DDR_BASE, PAGE_SIZE); + assert(ret == 0); + + stm32mp_clk_disable(BKPSRAM); +} +#endif + +void stm32_restore_ddr_training_area(void) +{ + struct backup_data_s *backup_data; + + stm32mp_clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + memcpy((uint32_t *)STM32MP_DDR_BASE, + &backup_data->ddr_training_backup, + TRAINING_AREA_SIZE); + dsb(); + + stm32mp_clk_disable(BKPSRAM); +} diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index a40852b..26a194a 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -17,12 +17,15 @@ #ifndef __ASSEMBLER__ #include #include +#include +#include #include #include #include #include #include +#include #include #include #endif @@ -56,6 +59,9 @@ #define STM32MP_SYSRAM_BASE U(0x2FFC0000) #define STM32MP_SYSRAM_SIZE U(0x00040000) +#define STM32MP_BACKUP_RAM_BASE U(0x54000000) +#define STM32MP_BACKUP_RAM_SIZE U(0x00001000) + /* DDR configuration */ #define STM32MP_DDR_BASE U(0xC0000000) #define STM32MP_DDR_MAX_SIZE U(0x40000000) /* Max 1GB */ @@ -336,6 +342,7 @@ * Device Tree defines ******************************************************************************/ #define DT_BSEC_COMPAT "st,stm32mp15-bsec" +#define DT_DDR_COMPAT "st,stm32mp1-ddr" #define DT_IWDG_COMPAT "st,stm32mp1-iwdg" #define DT_PWR_COMPAT "st,stm32mp1-pwr" #define DT_RCC_CLK_COMPAT "st,stm32mp1-rcc" diff --git a/plat/st/stm32mp1/stm32mp1_low_power.c b/plat/st/stm32mp1/stm32mp1_low_power.c new file mode 100644 index 0000000..36664c2 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_low_power.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static unsigned int gicc_pmr; +static struct stm32_rtc_calendar sleep_time, current_calendar; +static unsigned long long stdby_time_in_ms; +static bool enter_cstop_done; + +struct pwr_lp_config { + uint32_t pwr_cr1; + uint32_t pwr_mpucr; + const char *regul_suspend_node_name; +}; + +#define PWR_CR1_MASK (PWR_CR1_LPDS | PWR_CR1_LPCFG | PWR_CR1_LVDS) +#define PWR_MPUCR_MASK (PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF | PWR_MPUCR_PDDS) + +static const struct pwr_lp_config config_pwr[STM32_PM_MAX_SOC_MODE] = { + [STM32_PM_CSLEEP_RUN] = { + .pwr_cr1 = 0U, + .pwr_mpucr = PWR_MPUCR_CSSF, + .regul_suspend_node_name = NULL, + }, + [STM32_PM_CSTOP_ALLOW_STOP] = { + .pwr_cr1 = 0U, + .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF, + .regul_suspend_node_name = NULL, + }, + [STM32_PM_CSTOP_ALLOW_LP_STOP] = { + .pwr_cr1 = PWR_CR1_LPDS, + .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF, + .regul_suspend_node_name = "lp-stop", + }, + [STM32_PM_CSTOP_ALLOW_LPLV_STOP] = { + .pwr_cr1 = PWR_CR1_LVDS | PWR_CR1_LPDS | PWR_CR1_LPCFG, + .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF, + .regul_suspend_node_name = "lplv-stop", + }, + [STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR] = { + .pwr_cr1 = 0U, + .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF | + PWR_MPUCR_PDDS, + .regul_suspend_node_name = "standby-ddr-sr", + }, + [STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF] = { + .pwr_cr1 = 0U, + .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF | + PWR_MPUCR_PDDS, + .regul_suspend_node_name = "standby-ddr-off", + }, + [STM32_PM_SHUTDOWN] = { + .pwr_cr1 = 0U, + .pwr_mpucr = 0U, + .regul_suspend_node_name = "standby-ddr-off", + }, +}; + +#define GICC_PMR_PRIORITY_8 U(0x8) + +void stm32_apply_pmic_suspend_config(uint32_t mode) +{ + const char *node_name = config_pwr[mode].regul_suspend_node_name; + + assert(mode < ARRAY_SIZE(config_pwr)); + + if (node_name != NULL) { + if (!initialize_pmic_i2c()) { + panic(); + } + + if (dt_pmic_set_lp_config(node_name) != 0) { + panic(); + } + + if (dt_pmic_configure_boot_on_regulators() != 0) { + panic(); + } + } +} + +/* + * stm32_enter_cstop - Prepare CSTOP mode + * + * @mode - Target low power mode + * @nsec_addr - Non secure resume entry point + * Return 0 if succeed to suspend, non 0 else. + */ +static void enter_cstop(uint32_t mode, uint32_t nsec_addr) +{ + uint32_t zq0cr0_zdata; + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + uint32_t pwr_cr1 = config_pwr[mode].pwr_cr1; + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + stm32mp1_syscfg_disable_io_compensation(); + + dcsw_op_all(DC_OP_CISW); + + stm32_clean_context(); + + if (mode == STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR) { + /* + * The first 64 bytes of DDR need to be saved for DDR DQS + * training + */ + stm32_save_ddr_training_area(); + } + + if (dt_pmic_status() > 0) { + stm32_apply_pmic_suspend_config(mode); + + if (mode == STM32_PM_CSTOP_ALLOW_LP_STOP) { + pwr_cr1 |= PWR_CR1_LPCFG; + } + } + + /* Clear RCC interrupt before enabling it */ + mmio_setbits_32(rcc_base + RCC_MP_CIFR, RCC_MP_CIFR_WKUPF); + + /* Enable RCC Wake-up */ + mmio_setbits_32(rcc_base + RCC_MP_CIER, RCC_MP_CIFR_WKUPF); + + /* Configure low power mode */ + mmio_clrsetbits_32(pwr_base + PWR_MPUCR, PWR_MPUCR_MASK, + config_pwr[mode].pwr_mpucr); + mmio_clrsetbits_32(pwr_base + PWR_CR1, PWR_CR1_MASK, + pwr_cr1); + + /* Clear RCC pending interrupt flags */ + mmio_write_32(rcc_base + RCC_MP_CIFR, RCC_MP_CIFR_MASK); + + /* Request CSTOP mode to RCC */ + mmio_setbits_32(rcc_base + RCC_MP_SREQSETR, + RCC_MP_SREQSETR_STPREQ_P0 | RCC_MP_SREQSETR_STPREQ_P1); + + stm32_iwdg_refresh(); + + gicc_pmr = plat_ic_set_priority_mask(GICC_PMR_PRIORITY_8); + + /* + * Set DDR in Self-refresh, even if no return address is given. + * This is also the procedure awaited when switching off power supply. + */ + if (ddr_standby_sr_entry(&zq0cr0_zdata) != 0) { + return; + } + + stm32mp_clk_enable(RTCAPB); + + mmio_write_32(bkpr_core1_addr, 0); + mmio_write_32(bkpr_core1_magic, 0); + + if (mode == STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR) { + /* + * Save non-secure world entrypoint after standby in Backup + * register + */ + mmio_write_32(bkpr_core1_addr, nsec_addr); + mmio_write_32(bkpr_core1_magic, + BOOT_API_A7_CORE0_MAGIC_NUMBER); + + if (stm32_save_context(zq0cr0_zdata) != 0) { + panic(); + } + + /* Keep retention and backup RAM content in standby */ + mmio_setbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | + PWR_CR2_RREN); + while ((mmio_read_32(pwr_base + PWR_CR2) & + (PWR_CR2_BRRDY | PWR_CR2_RRRDY)) == 0U) { + ; + } + } + + stm32mp_clk_disable(RTCAPB); + + stm32_rtc_get_calendar(&sleep_time); + + enter_cstop_done = true; +} + +/* + * stm32_exit_cstop - Exit from CSTOP mode + */ +void stm32_exit_cstop(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + if (!enter_cstop_done) { + return; + } + + enter_cstop_done = false; + + if (ddr_sw_self_refresh_exit() != 0) { + panic(); + } + + plat_ic_set_priority_mask(gicc_pmr); + + /* Disable RCC Wake-up */ + mmio_clrbits_32(rcc_base + RCC_MP_CIER, RCC_MP_CIFR_WKUPF); + + /* Disable STOP request */ + mmio_setbits_32(rcc_base + RCC_MP_SREQCLRR, + RCC_MP_SREQSETR_STPREQ_P0 | RCC_MP_SREQSETR_STPREQ_P1); + + dsb(); + isb(); + + /* Disable retention and backup RAM content after stop */ + mmio_clrbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | PWR_CR2_RREN); + + /* Update STGEN counter with low power mode duration */ + stm32_rtc_get_calendar(¤t_calendar); + + stdby_time_in_ms = stm32_rtc_diff_calendar(¤t_calendar, + &sleep_time); + + stm32mp1_stgen_increment(stdby_time_in_ms); + + stm32mp1_syscfg_enable_io_compensation(); +} + +static void enter_shutdown(void) +{ + /* Set DDR in Self-refresh before shutting down the platform */ + if (ddr_standby_sr_entry(NULL) != 0) { + WARN("DDR can't be set in Self-refresh mode\n"); + } + + if (dt_pmic_status() > 0) { + if (!initialize_pmic_i2c()) { + panic(); + } + + stpmic1_switch_off(); + + udelay(100); + + /* Shouldn't be reached */ + panic(); + } +} + +static void enter_csleep(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + + mmio_clrsetbits_32(pwr_base + PWR_MPUCR, PWR_MPUCR_MASK, + config_pwr[STM32_PM_CSLEEP_RUN].pwr_mpucr); + mmio_clrsetbits_32(pwr_base + PWR_CR1, PWR_CR1_MASK, + config_pwr[STM32_PM_CSLEEP_RUN].pwr_cr1); + + stm32_pwr_down_wfi(); +} + +void stm32_enter_low_power(uint32_t mode, uint32_t nsec_addr) +{ + switch (mode) { + case STM32_PM_SHUTDOWN: + enter_shutdown(); + break; + + case STM32_PM_CSLEEP_RUN: + enter_csleep(); + break; + + default: + enter_cstop(mode, nsec_addr); + break; + } +} + +void stm32_pwr_down_wfi(void) +{ + uint32_t interrupt = GIC_SPURIOUS_INTERRUPT; + + while (interrupt == GIC_SPURIOUS_INTERRUPT) { + wfi(); + + interrupt = gicv2_acknowledge_interrupt(); + + if (interrupt != GIC_SPURIOUS_INTERRUPT) { + gicv2_end_of_interrupt(interrupt); + } + + stm32_iwdg_refresh(); + } +} diff --git a/plat/st/stm32mp1/stm32mp1_pm.c b/plat/st/stm32mp1/stm32mp1_pm.c index cf9fa8e..9ebea40 100644 --- a/plat/st/stm32mp1/stm32mp1_pm.c +++ b/plat/st/stm32mp1/stm32mp1_pm.c @@ -13,14 +13,20 @@ #include #include #include +#include #include +#include #include #include #include #include +#include +#include + static uintptr_t stm32_sec_entrypoint; static uint32_t cntfrq_core0; +static uintptr_t saved_entrypoint; /******************************************************************************* * STM32MP1 handler called when a CPU is about to enter standby. @@ -33,11 +39,12 @@ assert(cpu_state == ARM_LOCAL_STATE_RET); /* - * Enter standby state - * dsb is good practice before using wfi to enter low power states + * Enter standby state. + * Synchronize on memory accesses and instruction flow before the WFI + * instruction. */ - isb(); dsb(); + isb(); while (interrupt == GIC_SPURIOUS_INTERRUPT) { wfi(); @@ -64,10 +71,23 @@ uint32_t bkpr_core1_magic = tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + if (stm32mp_is_single_core()) { + return PSCI_E_INTERN_FAIL; + } + if (mpidr == current_cpu_mpidr) { return PSCI_E_INVALID_PARAMS; } + /* Reset backup register content */ + mmio_write_32(bkpr_core1_magic, 0); + + /* Need to send additional IT 0 after individual core 1 reset */ + gicv2_raise_sgi(ARM_IRQ_NON_SEC_SGI_0, STM32MP_SECONDARY_CPU); + + /* Wait for this IT to be acknowledged by ROM code. */ + udelay(10); + if ((stm32_sec_entrypoint < STM32MP_SYSRAM_BASE) || (stm32_sec_entrypoint > (STM32MP_SYSRAM_BASE + (STM32MP_SYSRAM_SIZE - 1)))) { @@ -107,7 +127,9 @@ ******************************************************************************/ static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state) { - /* Nothing to do, power domain is not disabled */ + uint32_t soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_SUSPEND); + + stm32_enter_low_power(soc_mode, saved_entrypoint); } /******************************************************************************* @@ -134,16 +156,59 @@ /* Nothing to do, power domain is not disabled */ } +/******************************************************************************* + * STM32MP1 handler called when a core tries to power itself down. If this + * call is made by core 0, it is a return from stop mode. In this case, we + * should restore previous context and jump to secure entrypoint. + ******************************************************************************/ static void __dead2 stm32_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) { - ERROR("stm32mpu1 Power Down WFI: operation not handled.\n"); + if (MPIDR_AFFLVL0_VAL(read_mpidr_el1()) == STM32MP_PRIMARY_CPU) { + void (*warm_entrypoint)(void) = + (void (*)(void))stm32_sec_entrypoint; + + stm32_pwr_down_wfi(); + + stm32_exit_cstop(); + + disable_mmu_icache_secure(); + + warm_entrypoint(); + } + + mmio_write_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, + RCC_MP_GRSTCSETR_MPUP1RST); + + /* + * Synchronize on memory accesses and instruction flow before + * auto-reset from the WFI instruction. + */ + dsb(); + isb(); + wfi(); + + /* This shouldn't be reached */ panic(); } static void __dead2 stm32_system_off(void) { - ERROR("stm32mpu1 System Off: operation not handled.\n"); + uint32_t soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_OFF); + + if (!stm32mp_is_single_core()) { + /* Prepare Core 1 reset */ + mmio_setbits_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, + RCC_MP_GRSTCSETR_MPUP1RST); + /* Send IT to core 1 to put itself in WFI */ + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_1, STM32MP_SECONDARY_CPU); + } + + stm32_enter_low_power(soc_mode, 0); + + stm32_pwr_down_wfi(); + + /* This shouldn't be reached */ panic(); } @@ -188,6 +253,8 @@ return PSCI_E_INVALID_ADDRESS; } + saved_entrypoint = entrypoint; + return PSCI_E_SUCCESS; } @@ -211,6 +278,12 @@ return (int)HW_ON; } +static void stm32_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + req_state->pwr_domain_state[0] = ARM_LOCAL_STATE_OFF; + req_state->pwr_domain_state[1] = ARM_LOCAL_STATE_OFF; +} + /******************************************************************************* * Export the platform handlers. The ARM Standard platform layer will take care * of registering the handlers with PSCI. @@ -227,7 +300,8 @@ .system_reset = stm32_system_reset, .validate_power_state = stm32_validate_power_state, .validate_ns_entrypoint = stm32_validate_ns_entrypoint, - .get_node_hw_state = stm32_node_hw_state + .get_node_hw_state = stm32_node_hw_state, + .get_sys_suspend_power_state = stm32_get_sys_suspend_power_state, }; /******************************************************************************* diff --git a/plat/st/stm32mp1/stm32mp1_power_config.c b/plat/st/stm32mp1/stm32mp1_power_config.c new file mode 100644 index 0000000..93644f4 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_power_config.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#define DT_PWR_COMPAT "st,stm32mp1-pwr" +#define SYSTEM_SUSPEND_SUPPORTED_MODES "system_suspend_supported_soc_modes" +#define SYSTEM_OFF_MODE "system_off_soc_mode" + +static uint32_t deepest_system_suspend_mode; +static uint32_t system_off_mode; +static uint8_t stm32mp1_supported_soc_modes[STM32_PM_MAX_SOC_MODE]; + +static int dt_get_pwr_node(void *fdt) +{ + return fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); +} + +static void save_supported_mode(void *fdt, int pwr_node) +{ + int len; + uint32_t count; + unsigned int i; + uint32_t supported[ARRAY_SIZE(stm32mp1_supported_soc_modes)]; + const void *prop; + + prop = fdt_getprop(fdt, pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES, &len); + if (prop == NULL) { + panic(); + } + + count = (uint32_t)len / sizeof(uint32_t); + if (count > STM32_PM_MAX_SOC_MODE) { + panic(); + } + + if (fdt_read_uint32_array(pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES, + &supported[0], count) < 0) { + ERROR("PWR DT\n"); + panic(); + } + + for (i = 0; i < count; i++) { + if (supported[i] >= STM32_PM_MAX_SOC_MODE) { + ERROR("Invalid mode\n"); + panic(); + } + stm32mp1_supported_soc_modes[supported[i]] = 1U; + } + + /* Initialize to deepest possible mode */ + for (i = STM32_PM_MAX_SOC_MODE - 1U; i != STM32_PM_CSLEEP_RUN; i--) { + if (stm32mp1_supported_soc_modes[i] == 1U) { + deepest_system_suspend_mode = i; + break; + } + } +} + +static int dt_fill_lp_state(uint32_t *lp_state_config, const char *lp_state) +{ + int pwr_node; + void *fdt; + const fdt32_t *cuint; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + pwr_node = dt_get_pwr_node(fdt); + if (pwr_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, pwr_node, lp_state, NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + *lp_state_config = fdt32_to_cpu(*cuint); + + save_supported_mode(fdt, pwr_node); + + return 0; +} + +void stm32mp1_init_lp_states(void) +{ + if (dt_fill_lp_state(&system_off_mode, SYSTEM_OFF_MODE) < 0) { + ERROR("Node %s not found\n", SYSTEM_OFF_MODE); + panic(); + } +} + +static bool is_allowed_mode(uint32_t soc_mode) +{ + assert(soc_mode < ARRAY_SIZE(stm32mp1_supported_soc_modes)); + + return stm32mp1_supported_soc_modes[soc_mode] == 1U; +} + +uint32_t stm32mp1_get_lp_soc_mode(uint32_t psci_mode) +{ + uint32_t mode; + + if (psci_mode == PSCI_MODE_SYSTEM_OFF) { + return system_off_mode; + } + + mode = deepest_system_suspend_mode; + + while ((mode > STM32_PM_CSLEEP_RUN) && !is_allowed_mode(mode)) { + mode--; + } + + return mode; +} diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c index e2dcd2a..3a7b22e 100644 --- a/plat/st/stm32mp1/stm32mp1_private.c +++ b/plat/st/stm32mp1/stm32mp1_private.c @@ -10,8 +10,11 @@ #include +#include +#include #include #include +#include /* Internal layout of the 32bit OTP word board_id */ #define BOARD_ID_BOARD_NB_MASK GENMASK(31, 16) @@ -76,6 +79,42 @@ enable_mmu_svc_mon(0); } +#define ARM_CNTXCTL_IMASK BIT(1) + +void stm32mp_mask_timer(void) +{ + /* Mask timer interrupts */ + write_cntp_ctl(read_cntp_ctl() | ARM_CNTXCTL_IMASK); + write_cntv_ctl(read_cntv_ctl() | ARM_CNTXCTL_IMASK); +} + +void __dead2 stm32mp_wait_cpu_reset(void) +{ + uint32_t id; + + dcsw_op_all(DC_OP_CISW); + write_sctlr(read_sctlr() & ~SCTLR_C_BIT); + dcsw_op_all(DC_OP_CISW); + __asm__("clrex"); + + dsb(); + isb(); + + for ( ; ; ) { + do { + id = plat_ic_get_pending_interrupt_id(); + + if (id <= MAX_SPI_ID) { + gicv2_end_of_interrupt(id); + + plat_ic_disable_interrupt(id); + } + } while (id <= MAX_SPI_ID); + + wfi(); + } +} + unsigned long stm32_get_gpio_bank_clock(unsigned int bank) { if (bank == GPIO_BANK_Z) { diff --git a/plat/st/stm32mp1/stm32mp1_shared_resources.c.bak b/plat/st/stm32mp1/stm32mp1_shared_resources.c.bak new file mode 100644 index 0000000..208e34a --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_shared_resources.c.bak @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include + +#include +#include +#include + +#include + +/* + * Once one starts to get the resource registering state, one cannot register + * new resources. This ensures resource state cannot change. + */ +static bool registering_locked; + +/* + * Shared peripherals and resources registration + * + * Each resource assignation is stored in a table. The state defaults + * to PERIPH_UNREGISTERED if the resource is not explicitly assigned. + * + * Resource driver that as not embedded (a.k.a their related CFG_xxx build + * directive is disabled) are assigned to the non-secure world. + * + * Each pin of the GPIOZ bank can be secure or non-secure. + * + * It is the platform responsibility the ensure resource assignation + * matches the access permission firewalls configuration. + */ +enum shres_state { + SHRES_UNREGISTERED = 0, + SHRES_SECURE, + SHRES_NON_SECURE, +}; + +/* Force uint8_t array for array of enum shres_state for size considerations */ +static uint8_t shres_state[STM32MP1_SHRES_COUNT]; + +static const char *shres2str_id_tbl[STM32MP1_SHRES_COUNT] __unused = { + [STM32MP1_SHRES_GPIOZ(0)] = "GPIOZ0", + [STM32MP1_SHRES_GPIOZ(1)] = "GPIOZ1", + [STM32MP1_SHRES_GPIOZ(2)] = "GPIOZ2", + [STM32MP1_SHRES_GPIOZ(3)] = "GPIOZ3", + [STM32MP1_SHRES_GPIOZ(4)] = "GPIOZ4", + [STM32MP1_SHRES_GPIOZ(5)] = "GPIOZ5", + [STM32MP1_SHRES_GPIOZ(6)] = "GPIOZ6", + [STM32MP1_SHRES_GPIOZ(7)] = "GPIOZ7", + [STM32MP1_SHRES_IWDG1] = "IWDG1", + [STM32MP1_SHRES_USART1] = "USART1", + [STM32MP1_SHRES_SPI6] = "SPI6", + [STM32MP1_SHRES_I2C4] = "I2C4", + [STM32MP1_SHRES_RNG1] = "RNG1", + [STM32MP1_SHRES_HASH1] = "HASH1", + [STM32MP1_SHRES_CRYP1] = "CRYP1", + [STM32MP1_SHRES_I2C6] = "I2C6", + [STM32MP1_SHRES_RTC] = "RTC", + [STM32MP1_SHRES_MCU] = "MCU", + [STM32MP1_SHRES_MDMA] = "MDMA", + [STM32MP1_SHRES_PLL3] = "PLL3", +}; + +static const char __unused *shres2str_id(enum stm32mp_shres id) +{ + assert(id < ARRAY_SIZE(shres2str_id_tbl)); + + return shres2str_id_tbl[id]; +} + +static const char __unused *shres2str_state_tbl[] = { + [SHRES_UNREGISTERED] = "unregistered", + [SHRES_NON_SECURE] = "non-secure", + [SHRES_SECURE] = "secure", +}; + +static const char __unused *shres2str_state(unsigned int state) +{ + assert(state < ARRAY_SIZE(shres2str_state_tbl)); + + return shres2str_state_tbl[state]; +} + +/* Get resource state: these accesses lock the registering support */ +static void lock_registering(void) +{ + registering_locked = true; +} + +static bool periph_is_non_secure(enum stm32mp_shres id) +{ + lock_registering(); + + return (shres_state[id] == SHRES_NON_SECURE) || + (shres_state[id] == SHRES_UNREGISTERED); +} + +static bool periph_is_secure(enum stm32mp_shres id) +{ + return !periph_is_non_secure(id); +} + +/* GPIOZ pin count is saved in RAM to prevent parsing FDT several times */ +static int8_t gpioz_nbpin = -1; + +static unsigned int get_gpio_nbpin(unsigned int bank) +{ + if (bank != GPIO_BANK_Z) { + int count = fdt_get_gpio_bank_pin_count(bank); + + assert((count >= 0) || (count <= (GPIO_PIN_MAX + 1))); + + return (unsigned int)count; + } + + if (gpioz_nbpin < 0) { + int count = fdt_get_gpio_bank_pin_count(GPIO_BANK_Z); + + assert((count == 0) || (count == STM32MP_GPIOZ_PIN_MAX_COUNT)); + + gpioz_nbpin = count; + } + + return (unsigned int)gpioz_nbpin; +} + +static unsigned int get_gpioz_nbpin(void) +{ + return get_gpio_nbpin(GPIO_BANK_Z); +} + +static void register_periph(enum stm32mp_shres id, unsigned int state) +{ + assert((id < STM32MP1_SHRES_COUNT) && + ((state == SHRES_SECURE) || (state == SHRES_NON_SECURE))); + + if (registering_locked) { + if (shres_state[id] == state) { + return; + } + panic(); + } + + if ((shres_state[id] != SHRES_UNREGISTERED) && + (shres_state[id] != state)) { + VERBOSE("Cannot change %s from %s to %s\n", + shres2str_id(id), + shres2str_state(shres_state[id]), + shres2str_state(state)); + panic(); + } + + if (shres_state[id] == SHRES_UNREGISTERED) { + VERBOSE("Register %s as %s\n", + shres2str_id(id), shres2str_state(state)); + } + + if ((id >= STM32MP1_SHRES_GPIOZ(0)) && + (id <= STM32MP1_SHRES_GPIOZ(7)) && + ((id - STM32MP1_SHRES_GPIOZ(0)) >= get_gpioz_nbpin())) { + ERROR("Invalid GPIO pin %u, %u pin(s) available\n", + id - STM32MP1_SHRES_GPIOZ(0), get_gpioz_nbpin()); + panic(); + } + + shres_state[id] = (uint8_t)state; + + /* Explore clock tree to lock dependencies */ + if (state == SHRES_SECURE) { + enum stm32mp_shres clock_res_id; + + switch (id) { + case STM32MP1_SHRES_GPIOZ(0): + case STM32MP1_SHRES_GPIOZ(1): + case STM32MP1_SHRES_GPIOZ(2): + case STM32MP1_SHRES_GPIOZ(3): + case STM32MP1_SHRES_GPIOZ(4): + case STM32MP1_SHRES_GPIOZ(5): + case STM32MP1_SHRES_GPIOZ(6): + case STM32MP1_SHRES_GPIOZ(7): + clock_res_id = GPIOZ; + break; + case STM32MP1_SHRES_IWDG1: + clock_res_id = IWDG1; + break; + case STM32MP1_SHRES_USART1: + clock_res_id = USART1_K; + break; + case STM32MP1_SHRES_SPI6: + clock_res_id = SPI6_K; + break; + case STM32MP1_SHRES_I2C4: + clock_res_id = I2C4_K; + break; + case STM32MP1_SHRES_RNG1: + clock_res_id = RNG1_K; + break; + case STM32MP1_SHRES_HASH1: + clock_res_id = HASH1; + break; + case STM32MP1_SHRES_CRYP1: + clock_res_id = CRYP1; + break; + case STM32MP1_SHRES_I2C6: + clock_res_id = I2C6_K; + break; + case STM32MP1_SHRES_RTC: + clock_res_id = RTC; + break; + default: + /* No clock resource dependency */ + return; + } + + stm32mp1_register_clock_parents_secure(clock_res_id); + } +} + +/* Register resource by ID */ +void stm32mp_register_secure_periph(enum stm32mp_shres id) +{ + register_periph(id, SHRES_SECURE); +} + +void stm32mp_register_non_secure_periph(enum stm32mp_shres id) +{ + register_periph(id, SHRES_NON_SECURE); +} + +static void register_periph_iomem(uintptr_t base, unsigned int state) +{ + enum stm32mp_shres id; + + switch (base) { + case CRYP1_BASE: + id = STM32MP1_SHRES_CRYP1; + break; + case HASH1_BASE: + id = STM32MP1_SHRES_HASH1; + break; + case I2C4_BASE: + id = STM32MP1_SHRES_I2C4; + break; + case I2C6_BASE: + id = STM32MP1_SHRES_I2C6; + break; + case IWDG1_BASE: + id = STM32MP1_SHRES_IWDG1; + break; + case RNG1_BASE: + id = STM32MP1_SHRES_RNG1; + break; + case RTC_BASE: + id = STM32MP1_SHRES_RTC; + break; + case SPI6_BASE: + id = STM32MP1_SHRES_SPI6; + break; + case USART1_BASE: + id = STM32MP1_SHRES_USART1; + break; + + case GPIOA_BASE: + case GPIOB_BASE: + case GPIOC_BASE: + case GPIOD_BASE: + case GPIOE_BASE: + case GPIOF_BASE: + case GPIOG_BASE: + case GPIOH_BASE: + case GPIOI_BASE: + case GPIOJ_BASE: + case GPIOK_BASE: + case USART2_BASE: + case USART3_BASE: + case UART4_BASE: + case UART5_BASE: + case USART6_BASE: + case UART7_BASE: + case UART8_BASE: + case IWDG2_BASE: + /* Allow drivers to register some non-secure resources */ + VERBOSE("IO for non-secure resource 0x%x\n", + (unsigned int)base); + if (state != SHRES_NON_SECURE) { + panic(); + } + + return; + + default: + panic(); + } + + register_periph(id, state); +} + +void stm32mp_register_secure_periph_iomem(uintptr_t base) +{ + register_periph_iomem(base, SHRES_SECURE); +} + +void stm32mp_register_non_secure_periph_iomem(uintptr_t base) +{ + register_periph_iomem(base, SHRES_NON_SECURE); +} + +void stm32mp_register_secure_gpio(unsigned int bank, unsigned int pin) +{ + switch (bank) { + case GPIO_BANK_Z: + register_periph(STM32MP1_SHRES_GPIOZ(pin), SHRES_SECURE); + break; + default: + ERROR("GPIO bank %u cannot be secured\n", bank); + panic(); + } +} + +void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin) +{ + switch (bank) { + case GPIO_BANK_Z: + register_periph(STM32MP1_SHRES_GPIOZ(pin), SHRES_NON_SECURE); + break; + default: + break; + } +} + +static bool stm32mp_gpio_bank_is_non_secure(unsigned int bank) +{ + unsigned int non_secure = 0U; + unsigned int i; + + lock_registering(); + + if (bank != GPIO_BANK_Z) { + return true; + } + + for (i = 0U; i < get_gpioz_nbpin(); i++) { + if (periph_is_non_secure(STM32MP1_SHRES_GPIOZ(i))) { + non_secure++; + } + } + + return non_secure == get_gpioz_nbpin(); +} + +static bool stm32mp_gpio_bank_is_secure(unsigned int bank) +{ + unsigned int secure = 0U; + unsigned int i; + + lock_registering(); + + if (bank != GPIO_BANK_Z) { + return false; + } + + for (i = 0U; i < get_gpioz_nbpin(); i++) { + if (periph_is_secure(STM32MP1_SHRES_GPIOZ(i))) { + secure++; + } + } + + return secure == get_gpioz_nbpin(); +} + +bool stm32mp_nsec_can_access_clock(unsigned long clock_id) +{ + enum stm32mp_shres shres_id = STM32MP1_SHRES_COUNT; + + switch (clock_id) { + case CK_CSI: + case CK_HSE: + case CK_HSE_DIV2: + case CK_HSI: + case CK_LSE: + case CK_LSI: + case PLL1_P: + case PLL1_Q: + case PLL1_R: + case PLL2_P: + case PLL2_Q: + case PLL2_R: + case PLL3_P: + case PLL3_Q: + case PLL3_R: + case RTCAPB: + return true; + case GPIOZ: + /* Allow clock access if at least one pin is non-secure */ + return !stm32mp_gpio_bank_is_secure(GPIO_BANK_Z); + case CRYP1: + shres_id = STM32MP1_SHRES_CRYP1; + break; + case HASH1: + shres_id = STM32MP1_SHRES_HASH1; + break; + case I2C4_K: + shres_id = STM32MP1_SHRES_I2C4; + break; + case I2C6_K: + shres_id = STM32MP1_SHRES_I2C6; + break; + case IWDG1: + shres_id = STM32MP1_SHRES_IWDG1; + break; + case RNG1_K: + shres_id = STM32MP1_SHRES_RNG1; + break; + case RTC: + shres_id = STM32MP1_SHRES_RTC; + break; + case SPI6_K: + shres_id = STM32MP1_SHRES_SPI6; + break; + case USART1_K: + shres_id = STM32MP1_SHRES_USART1; + break; + default: + return false; + } + + return periph_is_non_secure(shres_id); +} + +bool stm32mp_nsec_can_access_reset(unsigned int reset_id) +{ + enum stm32mp_shres shres_id = STM32MP1_SHRES_COUNT; + + switch (reset_id) { + case CRYP1_R: + shres_id = STM32MP1_SHRES_CRYP1; + break; + case GPIOZ_R: + /* GPIOZ reset mandates all pins are non-secure */ + return stm32mp_gpio_bank_is_non_secure(GPIO_BANK_Z); + case HASH1_R: + shres_id = STM32MP1_SHRES_HASH1; + break; + case I2C4_R: + shres_id = STM32MP1_SHRES_I2C4; + break; + case I2C6_R: + shres_id = STM32MP1_SHRES_I2C6; + break; + case MCU_R: + shres_id = STM32MP1_SHRES_MCU; + break; + case MDMA_R: + shres_id = STM32MP1_SHRES_MDMA; + break; + case RNG1_R: + shres_id = STM32MP1_SHRES_RNG1; + break; + case SPI6_R: + shres_id = STM32MP1_SHRES_SPI6; + break; + case USART1_R: + shres_id = STM32MP1_SHRES_USART1; + break; + default: + return false; + } + + return periph_is_non_secure(shres_id); +} + +static bool mckprot_protects_periph(enum stm32mp_shres id) +{ + switch (id) { + case STM32MP1_SHRES_MCU: + case STM32MP1_SHRES_PLL3: + return true; + default: + return false; + } +} + +/* ETZPC configuration at drivers initialization completion */ +static enum etzpc_decprot_attributes shres2decprot_attr(enum stm32mp_shres id) +{ + assert((id < STM32MP1_SHRES_GPIOZ(0)) || + (id > STM32MP1_SHRES_GPIOZ(7))); + + if (periph_is_non_secure(id)) { + return ETZPC_DECPROT_NS_RW; + } + + return ETZPC_DECPROT_S_RW; +} + +static void set_etzpc_secure_configuration(void) +{ + /* Some system peripherals shall be secure */ + etzpc_configure_decprot(STM32MP1_ETZPC_STGENC_ID, ETZPC_DECPROT_S_RW); + etzpc_configure_decprot(STM32MP1_ETZPC_BKPSRAM_ID, ETZPC_DECPROT_S_RW); + etzpc_configure_decprot(STM32MP1_ETZPC_DDRCTRL_ID, + ETZPC_DECPROT_NS_R_S_W); + etzpc_configure_decprot(STM32MP1_ETZPC_DDRPHYC_ID, + ETZPC_DECPROT_NS_R_S_W); + + /* Configure ETZPC with peripheral registering */ + etzpc_configure_decprot(STM32MP1_ETZPC_CRYP1_ID, + shres2decprot_attr(STM32MP1_SHRES_CRYP1)); + etzpc_configure_decprot(STM32MP1_ETZPC_HASH1_ID, + shres2decprot_attr(STM32MP1_SHRES_HASH1)); + etzpc_configure_decprot(STM32MP1_ETZPC_I2C4_ID, + shres2decprot_attr(STM32MP1_SHRES_I2C4)); + etzpc_configure_decprot(STM32MP1_ETZPC_I2C6_ID, + shres2decprot_attr(STM32MP1_SHRES_I2C6)); + etzpc_configure_decprot(STM32MP1_ETZPC_IWDG1_ID, + shres2decprot_attr(STM32MP1_SHRES_IWDG1)); + etzpc_configure_decprot(STM32MP1_ETZPC_RNG1_ID, + shres2decprot_attr(STM32MP1_SHRES_RNG1)); + etzpc_configure_decprot(STM32MP1_ETZPC_USART1_ID, + shres2decprot_attr(STM32MP1_SHRES_USART1)); + etzpc_configure_decprot(STM32MP1_ETZPC_SPI6_ID, + shres2decprot_attr(STM32MP1_SHRES_SPI6)); +} + +static void check_rcc_secure_configuration(void) +{ + uint32_t n; + uint32_t error = 0U; + bool mckprot = stm32mp1_rcc_is_mckprot(); + bool secure = stm32mp1_rcc_is_secure(); + + for (n = 0U; n < ARRAY_SIZE(shres_state); n++) { + if (shres_state[n] != SHRES_SECURE) { + continue; + } + + if (!secure || (mckprot_protects_periph(n) && (!mckprot))) { + ERROR("RCC %s MCKPROT %s and %s secure\n", + secure ? "secure" : "non-secure", + mckprot ? "set" : "not set", + shres2str_id(n)); + error++; + } + } + + if (error != 0U) { + panic(); + } +} + +static void set_gpio_secure_configuration(void) +{ + uint32_t pin; + + for (pin = 0U; pin < get_gpioz_nbpin(); pin++) { + bool secure_state = periph_is_secure(STM32MP1_SHRES_GPIOZ(pin)); + + set_gpio_secure_cfg(GPIO_BANK_Z, pin, secure_state); + } +} + +static void print_shared_resources_state(void) +{ + unsigned int id; + + for (id = 0U; id < STM32MP1_SHRES_COUNT; id++) { + switch (shres_state[id]) { + case SHRES_SECURE: + INFO("stm32mp1 %s is secure\n", shres2str_id(id)); + break; + case SHRES_NON_SECURE: + case SHRES_UNREGISTERED: + VERBOSE("stm32mp %s is non-secure\n", shres2str_id(id)); + break; + default: + VERBOSE("stm32mp %s is invalid\n", shres2str_id(id)); + panic(); + } + } +} + +void stm32mp_lock_periph_registering(void) +{ + registering_locked = true; + + print_shared_resources_state(); + + check_rcc_secure_configuration(); + set_etzpc_secure_configuration(); + set_gpio_secure_configuration(); +}