diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c new file mode 100644 index 0000000..eed1d76 --- /dev/null +++ b/drivers/st/ddr/stm32mp1_ddr.c @@ -0,0 +1,895 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct reg_desc { + const char *name; + uint16_t offset; /* Offset for base address */ + uint8_t par_offset; /* Offset for parameter array */ +}; + +#define INVALID_OFFSET 0xFFU + +#define TIMESLOT_1US (plat_get_syscnt_freq2() / 1000000U) + +#define DDRCTL_REG(x, y) \ + { \ + .name = #x, \ + .offset = offsetof(struct stm32mp1_ddrctl, x), \ + .par_offset = offsetof(struct y, x) \ + } + +#define DDRPHY_REG(x, y) \ + { \ + .name = #x, \ + .offset = offsetof(struct stm32mp1_ddrphy, x), \ + .par_offset = offsetof(struct y, x) \ + } + +#define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg) +static const struct reg_desc ddr_reg[] = { + DDRCTL_REG_REG(mstr), + DDRCTL_REG_REG(mrctrl0), + DDRCTL_REG_REG(mrctrl1), + DDRCTL_REG_REG(derateen), + DDRCTL_REG_REG(derateint), + DDRCTL_REG_REG(pwrctl), + DDRCTL_REG_REG(pwrtmg), + DDRCTL_REG_REG(hwlpctl), + DDRCTL_REG_REG(rfshctl0), + DDRCTL_REG_REG(rfshctl3), + DDRCTL_REG_REG(crcparctl0), + DDRCTL_REG_REG(zqctl0), + DDRCTL_REG_REG(dfitmg0), + DDRCTL_REG_REG(dfitmg1), + DDRCTL_REG_REG(dfilpcfg0), + DDRCTL_REG_REG(dfiupd0), + DDRCTL_REG_REG(dfiupd1), + DDRCTL_REG_REG(dfiupd2), + DDRCTL_REG_REG(dfiphymstr), + DDRCTL_REG_REG(odtmap), + DDRCTL_REG_REG(dbg0), + DDRCTL_REG_REG(dbg1), + DDRCTL_REG_REG(dbgcmd), + DDRCTL_REG_REG(poisoncfg), + DDRCTL_REG_REG(pccfg), +}; + +#define DDRCTL_REG_TIMING(x) DDRCTL_REG(x, stm32mp1_ddrctrl_timing) +static const struct reg_desc ddr_timing[] = { + DDRCTL_REG_TIMING(rfshtmg), + DDRCTL_REG_TIMING(dramtmg0), + DDRCTL_REG_TIMING(dramtmg1), + DDRCTL_REG_TIMING(dramtmg2), + DDRCTL_REG_TIMING(dramtmg3), + DDRCTL_REG_TIMING(dramtmg4), + DDRCTL_REG_TIMING(dramtmg5), + DDRCTL_REG_TIMING(dramtmg6), + DDRCTL_REG_TIMING(dramtmg7), + DDRCTL_REG_TIMING(dramtmg8), + DDRCTL_REG_TIMING(dramtmg14), + DDRCTL_REG_TIMING(odtcfg), +}; + +#define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp1_ddrctrl_map) +static const struct reg_desc ddr_map[] = { + DDRCTL_REG_MAP(addrmap1), + DDRCTL_REG_MAP(addrmap2), + DDRCTL_REG_MAP(addrmap3), + DDRCTL_REG_MAP(addrmap4), + DDRCTL_REG_MAP(addrmap5), + DDRCTL_REG_MAP(addrmap6), + DDRCTL_REG_MAP(addrmap9), + DDRCTL_REG_MAP(addrmap10), + DDRCTL_REG_MAP(addrmap11), +}; + +#define DDRCTL_REG_PERF(x) DDRCTL_REG(x, stm32mp1_ddrctrl_perf) +static const struct reg_desc ddr_perf[] = { + DDRCTL_REG_PERF(sched), + DDRCTL_REG_PERF(sched1), + DDRCTL_REG_PERF(perfhpr1), + DDRCTL_REG_PERF(perflpr1), + DDRCTL_REG_PERF(perfwr1), + DDRCTL_REG_PERF(pcfgr_0), + DDRCTL_REG_PERF(pcfgw_0), + DDRCTL_REG_PERF(pcfgqos0_0), + DDRCTL_REG_PERF(pcfgqos1_0), + DDRCTL_REG_PERF(pcfgwqos0_0), + DDRCTL_REG_PERF(pcfgwqos1_0), + DDRCTL_REG_PERF(pcfgr_1), + DDRCTL_REG_PERF(pcfgw_1), + DDRCTL_REG_PERF(pcfgqos0_1), + DDRCTL_REG_PERF(pcfgqos1_1), + DDRCTL_REG_PERF(pcfgwqos0_1), + DDRCTL_REG_PERF(pcfgwqos1_1), +}; + +#define DDRPHY_REG_REG(x) DDRPHY_REG(x, stm32mp1_ddrphy_reg) +static const struct reg_desc ddrphy_reg[] = { + DDRPHY_REG_REG(pgcr), + DDRPHY_REG_REG(aciocr), + DDRPHY_REG_REG(dxccr), + DDRPHY_REG_REG(dsgcr), + DDRPHY_REG_REG(dcr), + DDRPHY_REG_REG(odtcr), + DDRPHY_REG_REG(zq0cr1), + DDRPHY_REG_REG(dx0gcr), + DDRPHY_REG_REG(dx1gcr), + DDRPHY_REG_REG(dx2gcr), + DDRPHY_REG_REG(dx3gcr), +}; + +#define DDRPHY_REG_TIMING(x) DDRPHY_REG(x, stm32mp1_ddrphy_timing) +static const struct reg_desc ddrphy_timing[] = { + DDRPHY_REG_TIMING(ptr0), + DDRPHY_REG_TIMING(ptr1), + DDRPHY_REG_TIMING(ptr2), + DDRPHY_REG_TIMING(dtpr0), + DDRPHY_REG_TIMING(dtpr1), + DDRPHY_REG_TIMING(dtpr2), + DDRPHY_REG_TIMING(mr0), + DDRPHY_REG_TIMING(mr1), + DDRPHY_REG_TIMING(mr2), + DDRPHY_REG_TIMING(mr3), +}; + +#define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal) +static const struct reg_desc ddrphy_cal[] = { + DDRPHY_REG_CAL(dx0dllcr), + DDRPHY_REG_CAL(dx0dqtr), + DDRPHY_REG_CAL(dx0dqstr), + DDRPHY_REG_CAL(dx1dllcr), + DDRPHY_REG_CAL(dx1dqtr), + DDRPHY_REG_CAL(dx1dqstr), + DDRPHY_REG_CAL(dx2dllcr), + DDRPHY_REG_CAL(dx2dqtr), + DDRPHY_REG_CAL(dx2dqstr), + DDRPHY_REG_CAL(dx3dllcr), + DDRPHY_REG_CAL(dx3dqtr), + DDRPHY_REG_CAL(dx3dqstr), +}; + +#define DDR_REG_DYN(x) \ + { \ + .name = #x, \ + .offset = offsetof(struct stm32mp1_ddrctl, x), \ + .par_offset = INVALID_OFFSET \ + } + +static const struct reg_desc ddr_dyn[] = { + DDR_REG_DYN(stat), + DDR_REG_DYN(init0), + DDR_REG_DYN(dfimisc), + DDR_REG_DYN(dfistat), + DDR_REG_DYN(swctl), + DDR_REG_DYN(swstat), + DDR_REG_DYN(pctrl_0), + DDR_REG_DYN(pctrl_1), +}; + +#define DDRPHY_REG_DYN(x) \ + { \ + .name = #x, \ + .offset = offsetof(struct stm32mp1_ddrphy, x), \ + .par_offset = INVALID_OFFSET \ + } + +static const struct reg_desc ddrphy_dyn[] = { + DDRPHY_REG_DYN(pir), + DDRPHY_REG_DYN(pgsr), +}; + +enum reg_type { + REG_REG, + REG_TIMING, + REG_PERF, + REG_MAP, + REGPHY_REG, + REGPHY_TIMING, + REGPHY_CAL, +/* + * Dynamic registers => managed in driver or not changed, + * can be dumped in interactive mode. + */ + REG_DYN, + REGPHY_DYN, + REG_TYPE_NB +}; + +enum base_type { + DDR_BASE, + DDRPHY_BASE, + NONE_BASE +}; + +struct ddr_reg_info { + const char *name; + const struct reg_desc *desc; + uint8_t size; + enum base_type base; +}; + +static const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { + [REG_REG] = { + "static", ddr_reg, ARRAY_SIZE(ddr_reg), DDR_BASE + }, + [REG_TIMING] = { + "timing", ddr_timing, ARRAY_SIZE(ddr_timing), DDR_BASE + }, + [REG_PERF] = { + "perf", ddr_perf, ARRAY_SIZE(ddr_perf), DDR_BASE + }, + [REG_MAP] = { + "map", ddr_map, ARRAY_SIZE(ddr_map), DDR_BASE + }, + [REGPHY_REG] = { + "static", ddrphy_reg, ARRAY_SIZE(ddrphy_reg), DDRPHY_BASE + }, + [REGPHY_TIMING] = { + "timing", ddrphy_timing, ARRAY_SIZE(ddrphy_timing), DDRPHY_BASE + }, + [REGPHY_CAL] = { + "cal", ddrphy_cal, ARRAY_SIZE(ddrphy_cal), DDRPHY_BASE + }, + [REG_DYN] = { + "dyn", ddr_dyn, ARRAY_SIZE(ddr_dyn), DDR_BASE + }, + [REGPHY_DYN] = { + "dyn", ddrphy_dyn, ARRAY_SIZE(ddrphy_dyn), DDRPHY_BASE + }, +}; + +static uint32_t get_base_addr(const struct ddr_info *priv, enum base_type base) +{ + if (base == DDRPHY_BASE) { + return (uint32_t)priv->phy; + } else { + return (uint32_t)priv->ctl; + } +} + +static void set_reg(const struct ddr_info *priv, + enum reg_type type, + const void *param) +{ + unsigned int i; + unsigned int *ptr, value; + enum base_type base = ddr_registers[type].base; + uint32_t base_addr = get_base_addr(priv, base); + const struct reg_desc *desc = ddr_registers[type].desc; + + VERBOSE("init %s\n", ddr_registers[type].name); + for (i = 0; i < ddr_registers[type].size; i++) { + ptr = (unsigned int *)(base_addr + desc[i].offset); + if (desc[i].par_offset == INVALID_OFFSET) { + ERROR("invalid parameter offset for %s", desc[i].name); + panic(); + } else { + value = *((uint32_t *)((uint32_t)param + + desc[i].par_offset)); + mmio_write_32((uint32_t)ptr, value); + } + } +} + +static void stm32mp1_ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) +{ + uint32_t pgsr; + int error = 0; + unsigned long start; + unsigned long time0, time; + + start = get_timer(0); + time0 = start; + + do { + pgsr = mmio_read_32((uint32_t)&phy->pgsr); + time = get_timer(start); + if (time != time0) { + VERBOSE(" > [0x%x] pgsr = 0x%x &\n", + (uint32_t)&phy->pgsr, pgsr); + VERBOSE(" [0x%x] pir = 0x%x (time=%x)\n", + (uint32_t)&phy->pir, + mmio_read_32((uint32_t)&phy->pir), + (uint32_t)time); + } + + time0 = time; + if (time > plat_get_syscnt_freq2()) { + panic(); + } + if ((pgsr & DDRPHYC_PGSR_DTERR) != 0U) { + VERBOSE("DQS Gate Trainig Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_DTIERR) != 0U) { + VERBOSE("DQS Gate Trainig Intermittent Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_DFTERR) != 0U) { + VERBOSE("DQS Drift Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_RVERR) != 0U) { + VERBOSE("Read Valid Training Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_RVEIRR) != 0U) { + VERBOSE("Read Valid Training Intermittent Error\n"); + error++; + } + } while ((pgsr & DDRPHYC_PGSR_IDONE) == 0U && error == 0); + VERBOSE("\n[0x%x] pgsr = 0x%x\n", + (uint32_t)&phy->pgsr, pgsr); +} + +static void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, uint32_t pir) +{ + uint32_t pir_init = pir | DDRPHYC_PIR_INIT; + + mmio_write_32((uint32_t)&phy->pir, pir_init); + VERBOSE("[0x%x] pir = 0x%x -> 0x%x\n", + (uint32_t)&phy->pir, pir_init, + mmio_read_32((uint32_t)&phy->pir)); + + /* Need to wait 10 configuration clock before start polling */ + udelay(10); + + /* Wait DRAM initialization and Gate Training Evaluation complete */ + stm32mp1_ddrphy_idone_wait(phy); +} + +/* Start quasi dynamic register update */ +static void stm32mp1_start_sw_done(struct stm32mp1_ddrctl *ctl) +{ + mmio_clrbits_32((uint32_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + VERBOSE("[0x%x] swctl = 0x%x\n", + (uint32_t)&ctl->swctl, mmio_read_32((uint32_t)&ctl->swctl)); +} + +/* Wait quasi dynamic register update */ +static void stm32mp1_wait_sw_done_ack(struct stm32mp1_ddrctl *ctl) +{ + unsigned long start; + uint32_t swstat; + + mmio_setbits_32((uint32_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + VERBOSE("[0x%x] swctl = 0x%x\n", + (uint32_t)&ctl->swctl, mmio_read_32((uint32_t)&ctl->swctl)); + + start = get_timer(0); + do { + swstat = mmio_read_32((uint32_t)&ctl->swstat); + VERBOSE("[0x%x] swstat = 0x%x ", + (uint32_t)&ctl->swstat, swstat); + VERBOSE("timer in ms 0x%x = start 0x%lx\r", + get_timer(0), start); + if (get_timer(start) > plat_get_syscnt_freq2()) { + panic(); + } + } while ((swstat & DDRCTRL_SWSTAT_SW_DONE_ACK) == 0U); + + VERBOSE("[0x%x] swstat = 0x%x\n", + (uint32_t)&ctl->swstat, swstat); +} + +/* Wait quasi dynamic register update */ +static void stm32mp1_wait_operating_mode(struct ddr_info *priv, uint32_t mode) +{ + unsigned long start; + uint32_t stat; + uint32_t operating_mode; + uint32_t selref_type; + int break_loop = 0; + + start = get_timer(0); + for ( ; ; ) { + stat = mmio_read_32((uint32_t)&priv->ctl->stat); + operating_mode = stat & DDRCTRL_STAT_OPERATING_MODE_MASK; + selref_type = stat & DDRCTRL_STAT_SELFREF_TYPE_MASK; + VERBOSE("[0x%x] stat = 0x%x\n", + (uint32_t)&priv->ctl->stat, stat); + VERBOSE("timer in ms 0x%x = start 0x%lx\r", + get_timer(0), start); + if (get_timer(start) > plat_get_syscnt_freq2()) { + panic(); + } + + if (mode == DDRCTRL_STAT_OPERATING_MODE_SR) { + /* + * Self-refresh due to software + * => checking also STAT.selfref_type. + */ + if ((operating_mode == + DDRCTRL_STAT_OPERATING_MODE_SR) && + (selref_type == DDRCTRL_STAT_SELFREF_TYPE_SR)) { + break_loop = 1; + } + } else if (operating_mode == mode) { + break_loop = 1; + } else if ((mode == DDRCTRL_STAT_OPERATING_MODE_NORMAL) && + (operating_mode == DDRCTRL_STAT_OPERATING_MODE_SR) && + (selref_type == DDRCTRL_STAT_SELFREF_TYPE_ASR)) { + /* Normal mode: handle also automatic self refresh */ + break_loop = 1; + } + + if (break_loop == 1) { + break; + } + } + + VERBOSE("[0x%x] stat = 0x%x\n", + (uint32_t)&priv->ctl->stat, stat); +} + +/* Mode Register Writes (MRW or MRS) */ +static void stm32mp1_mode_register_write(struct ddr_info *priv, uint8_t addr, + uint32_t data) +{ + uint32_t mrctrl0; + + VERBOSE("MRS: %d = %x\n", addr, data); + + /* + * 1. Poll MRSTAT.mr_wr_busy until it is '0'. + * This checks that there is no outstanding MR transaction. + * No write should be performed to MRCTRL0 and MRCTRL1 + * if MRSTAT.mr_wr_busy = 1. + */ + while ((mmio_read_32((uint32_t)&priv->ctl->mrstat) & + DDRCTRL_MRSTAT_MR_WR_BUSY) != 0U) { + ; + } + + /* + * 2. Write the MRCTRL0.mr_type, MRCTRL0.mr_addr, MRCTRL0.mr_rank + * and (for MRWs) MRCTRL1.mr_data to define the MR transaction. + */ + mrctrl0 = DDRCTRL_MRCTRL0_MR_TYPE_WRITE | + DDRCTRL_MRCTRL0_MR_RANK_ALL | + (((uint32_t)addr << DDRCTRL_MRCTRL0_MR_ADDR_SHIFT) & + DDRCTRL_MRCTRL0_MR_ADDR_MASK); + mmio_write_32((uint32_t)&priv->ctl->mrctrl0, mrctrl0); + VERBOSE("[0x%x] mrctrl0 = 0x%x (0x%x)\n", + (uint32_t)&priv->ctl->mrctrl0, + mmio_read_32((uint32_t)&priv->ctl->mrctrl0), mrctrl0); + mmio_write_32((uint32_t)&priv->ctl->mrctrl1, data); + VERBOSE("[0x%x] mrctrl1 = 0x%x\n", + (uint32_t)&priv->ctl->mrctrl1, + mmio_read_32((uint32_t)&priv->ctl->mrctrl1)); + + /* + * 3. In a separate APB transaction, write the MRCTRL0.mr_wr to 1. This + * bit is self-clearing, and triggers the MR transaction. + * The uMCTL2 then asserts the MRSTAT.mr_wr_busy while it performs + * the MR transaction to SDRAM, and no further access can be + * initiated until it is deasserted. + */ + mrctrl0 |= DDRCTRL_MRCTRL0_MR_WR; + mmio_write_32((uint32_t)&priv->ctl->mrctrl0, mrctrl0); + + while ((mmio_read_32((uint32_t)&priv->ctl->mrstat) & + DDRCTRL_MRSTAT_MR_WR_BUSY) != 0U) { + ; + } + + VERBOSE("[0x%x] mrctrl0 = 0x%x\n", + (uint32_t)&priv->ctl->mrctrl0, mrctrl0); +} + +/* Switch DDR3 from DLL-on to DLL-off */ +static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) +{ + uint32_t mr1 = mmio_read_32((uint32_t)&priv->phy->mr1); + uint32_t mr2 = mmio_read_32((uint32_t)&priv->phy->mr2); + uint32_t dbgcam; + + VERBOSE("mr1: 0x%x\n", mr1); + VERBOSE("mr2: 0x%x\n", mr2); + + /* + * 1. Set the DBG1.dis_hif = 1. + * This prevents further reads/writes being received on the HIF. + */ + mmio_setbits_32((uint32_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%x] dbg1 = 0x%x\n", + (uint32_t)&priv->ctl->dbg1, + mmio_read_32((uint32_t)&priv->ctl->dbg1)); + + /* + * 2. Ensure all commands have been flushed from the uMCTL2 by polling + * DBGCAM.wr_data_pipeline_empty = 1, + * DBGCAM.rd_data_pipeline_empty = 1, + * DBGCAM.dbg_wr_q_depth = 0 , + * DBGCAM.dbg_lpr_q_depth = 0, and + * DBGCAM.dbg_hpr_q_depth = 0. + */ + do { + dbgcam = mmio_read_32((uint32_t)&priv->ctl->dbgcam); + VERBOSE("[0x%x] dbgcam = 0x%x\n", + (uint32_t)&priv->ctl->dbgcam, dbgcam); + } while ((((dbgcam & DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY) == + DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY)) && + ((dbgcam & DDRCTRL_DBGCAM_DBG_Q_DEPTH) == 0U)); + + /* + * 3. Perform an MRS command (using MRCTRL0 and MRCTRL1 registers) + * to disable RTT_NOM: + * a. DDR3: Write to MR1[9], MR1[6] and MR1[2] + * b. DDR4: Write to MR1[10:8] + */ + mr1 &= ~(BIT(9) | BIT(6) | BIT(2)); + stm32mp1_mode_register_write(priv, 1, mr1); + + /* + * 4. For DDR4 only: Perform an MRS command + * (using MRCTRL0 and MRCTRL1 registers) to write to MR5[8:6] + * to disable RTT_PARK + */ + + /* + * 5. Perform an MRS command (using MRCTRL0 and MRCTRL1 registers) + * to write to MR2[10:9], to disable RTT_WR + * (and therefore disable dynamic ODT). + * This applies for both DDR3 and DDR4. + */ + mr2 &= ~GENMASK(10, 9); + stm32mp1_mode_register_write(priv, 2, mr2); + + /* + * 6. Perform an MRS command (using MRCTRL0 and MRCTRL1 registers) + * to disable the DLL. The timing of this MRS is automatically + * handled by the uMCTL2. + * a. DDR3: Write to MR1[0] + * b. DDR4: Write to MR1[0] + */ + mr1 |= BIT(0); + stm32mp1_mode_register_write(priv, 1, mr1); + + /* + * 7. Put the SDRAM into self-refresh mode by setting + * PWRCTL.selfref_sw = 1, and polling STAT.operating_mode to ensure + * the DDRC has entered self-refresh. + */ + mmio_setbits_32((uint32_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + VERBOSE("[0x%x] pwrctl = 0x%x\n", + (uint32_t)&priv->ctl->pwrctl, + mmio_read_32((uint32_t)&priv->ctl->pwrctl)); + + /* + * 8. Wait until STAT.operating_mode[1:0]==11 indicating that the + * DWC_ddr_umctl2 core is in self-refresh mode. + * Ensure transition to self-refresh was due to software + * by checking that STAT.selfref_type[1:0]=2. + */ + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_SR); + + /* + * 9. Set the MSTR.dll_off_mode = 1. + * warning: MSTR.dll_off_mode is a quasi-dynamic type 2 field + */ + stm32mp1_start_sw_done(priv->ctl); + + mmio_setbits_32((uint32_t)&priv->ctl->mstr, DDRCTRL_MSTR_DLL_OFF_MODE); + VERBOSE("[0x%x] mstr = 0x%x\n", + (uint32_t)&priv->ctl->mstr, + mmio_read_32((uint32_t)&priv->ctl->mstr)); + + stm32mp1_wait_sw_done_ack(priv->ctl); + + /* 10. Change the clock frequency to the desired value. */ + + /* + * 11. Update any registers which may be required to change for the new + * frequency. This includes static and dynamic registers. + * This includes both uMCTL2 registers and PHY registers. + */ + + /* Change Bypass Mode Frequency Range */ + if (stm32mp1_clk_get_rate(DDRPHYC) < 100000000U) { + mmio_clrbits_32((uint32_t)&priv->phy->dllgcr, + DDRPHYC_DLLGCR_BPS200); + } else { + mmio_setbits_32((uint32_t)&priv->phy->dllgcr, + DDRPHYC_DLLGCR_BPS200); + } + + mmio_setbits_32((uint32_t)&priv->phy->acdllcr, DDRPHYC_ACDLLCR_DLLDIS); + + mmio_setbits_32((uint32_t)&priv->phy->dx0dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + mmio_setbits_32((uint32_t)&priv->phy->dx1dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + mmio_setbits_32((uint32_t)&priv->phy->dx2dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + mmio_setbits_32((uint32_t)&priv->phy->dx3dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + + /* 12. Exit the self-refresh state by setting PWRCTL.selfref_sw = 0. */ + mmio_clrbits_32((uint32_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); + + /* + * 13. If ZQCTL0.dis_srx_zqcl = 0, the uMCTL2 performs a ZQCL command + * at this point. + */ + + /* + * 14. Perform MRS commands as required to re-program timing registers + * in the SDRAM for the new frequency + * (in particular, CL, CWL and WR may need to be changed). + */ + + /* 15. Write DBG1.dis_hif = 0 to re-enable reads and writes. */ + mmio_clrbits_32((uint32_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%x] dbg1 = 0x%x\n", + (uint32_t)&priv->ctl->dbg1, + mmio_read_32((uint32_t)&priv->ctl->dbg1)); +} + +static void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) +{ + stm32mp1_start_sw_done(ctl); + /* Quasi-dynamic register update*/ + mmio_setbits_32((uint32_t)&ctl->rfshctl3, + DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + mmio_clrbits_32((uint32_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + mmio_clrbits_32((uint32_t)&ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + stm32mp1_wait_sw_done_ack(ctl); +} + +static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, + uint32_t rfshctl3, uint32_t pwrctl) +{ + stm32mp1_start_sw_done(ctl); + if ((rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH) == 0U) { + mmio_clrbits_32((uint32_t)&ctl->rfshctl3, + DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + } + if ((pwrctl & DDRCTRL_PWRCTL_POWERDOWN_EN) != 0U) { + mmio_setbits_32((uint32_t)&ctl->pwrctl, + DDRCTRL_PWRCTL_POWERDOWN_EN); + } + mmio_setbits_32((uint32_t)&ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + stm32mp1_wait_sw_done_ack(ctl); +} + +static int board_ddr_power_init(enum ddr_type ddr_type) +{ + if (dt_check_pmic()) { + return pmic_ddr_power_init(ddr_type); + } + + return 0; +} + +void stm32mp1_ddr_init(struct ddr_info *priv, + struct stm32mp1_ddr_config *config) +{ + uint32_t pir; + int ret; + + if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { + ret = board_ddr_power_init(STM32MP_DDR3); + } else { + ret = board_ddr_power_init(STM32MP_LPDDR2); + } + + if (ret != 0) { + panic(); + } + + VERBOSE("name = %s\n", config->info.name); + VERBOSE("speed = %d MHz\n", config->info.speed); + VERBOSE("size = 0x%x\n", config->info.size); + + /* DDR INIT SEQUENCE */ + + /* + * 1. Program the DWC_ddr_umctl2 registers + * nota: check DFIMISC.dfi_init_complete = 0 + */ + + /* 1.1 RESETS: presetn, core_ddrc_rstn, aresetn */ + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST); + + /* 1.2. start CLOCK */ + if (stm32mp1_ddr_clk_enable(priv, config->info.speed) != 0) { + panic(); + } + + /* 1.3. deassert reset */ + /* De-assert PHY rstn and ctl_rstn via DPHYRST and DPHYCTLRST. */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST); + /* + * De-assert presetn once the clocks are active + * and stable via DDRCAPBRST bit. + */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); + + /* 1.4. wait 128 cycles to permit initialization of end logic */ + udelay(2); + /* For PCLK = 133MHz => 1 us is enough, 2 to allow lower frequency */ + + /* 1.5. initialize registers ddr_umctl2 */ + /* Stop uMCTL2 before PHY is ready */ + mmio_clrbits_32((uint32_t)&priv->ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + VERBOSE("[0x%x] dfimisc = 0x%x\n", + (uint32_t)&priv->ctl->dfimisc, + mmio_read_32((uint32_t)&priv->ctl->dfimisc)); + + set_reg(priv, REG_REG, &config->c_reg); + + /* DDR3 = don't set DLLOFF for init mode */ + if ((config->c_reg.mstr & + (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) + == (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) { + VERBOSE("deactivate DLL OFF in mstr\n"); + mmio_clrbits_32((uint32_t)&priv->ctl->mstr, + DDRCTRL_MSTR_DLL_OFF_MODE); + VERBOSE("[0x%x] mstr = 0x%x\n", + (uint32_t)&priv->ctl->mstr, + mmio_read_32((uint32_t)&priv->ctl->mstr)); + } + + set_reg(priv, REG_TIMING, &config->c_timing); + set_reg(priv, REG_MAP, &config->c_map); + + /* Skip CTRL init, SDRAM init is done by PHY PUBL */ + mmio_clrsetbits_32((uint32_t)&priv->ctl->init0, + DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK, + DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL); + VERBOSE("[0x%x] init0 = 0x%x\n", + (uint32_t)&priv->ctl->init0, + mmio_read_32((uint32_t)&priv->ctl->init0)); + + set_reg(priv, REG_PERF, &config->c_perf); + + /* 2. deassert reset signal core_ddrc_rstn, aresetn and presetn */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST); + + /* + * 3. start PHY init by accessing relevant PUBL registers + * (DXGCR, DCR, PTR*, MR*, DTPR*) + */ + set_reg(priv, REGPHY_REG, &config->p_reg); + set_reg(priv, REGPHY_TIMING, &config->p_timing); + set_reg(priv, REGPHY_CAL, &config->p_cal); + + /* DDR3 = don't set DLLOFF for init mode */ + if ((config->c_reg.mstr & + (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) + == (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) { + VERBOSE("deactivate DLL OFF in mr1\n"); + mmio_clrbits_32((uint32_t)&priv->phy->mr1, BIT(0)); + VERBOSE("[0x%x] mr1 = 0x%x\n", + (uint32_t)&priv->phy->mr1, + mmio_read_32((uint32_t)&priv->phy->mr1)); + } + + /* + * 4. Monitor PHY init status by polling PUBL register PGSR.IDONE + * Perform DDR PHY DRAM initialization and Gate Training Evaluation + */ + stm32mp1_ddrphy_idone_wait(priv->phy); + + /* + * 5. Indicate to PUBL that controller performs SDRAM initialization + * by setting PIR.INIT and PIR CTLDINIT and pool PGSR.IDONE + * DRAM init is done by PHY, init0.skip_dram.init = 1 + */ + + pir = DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | DDRPHYC_PIR_ZCAL | + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_DRAMINIT | DDRPHYC_PIR_ICPC; + + if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { + pir |= DDRPHYC_PIR_DRAMRST; /* Only for DDR3 */ + } + + stm32mp1_ddrphy_init(priv->phy, pir); + + /* + * 6. SET DFIMISC.dfi_init_complete_en to 1 + * Enable quasi-dynamic register programming. + */ + stm32mp1_start_sw_done(priv->ctl); + + mmio_setbits_32((uint32_t)&priv->ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + VERBOSE("[0x%x] dfimisc = 0x%x\n", + (uint32_t)&priv->ctl->dfimisc, + mmio_read_32((uint32_t)&priv->ctl->dfimisc)); + + stm32mp1_wait_sw_done_ack(priv->ctl); + + /* + * 7. Wait for DWC_ddr_umctl2 to move to normal operation mode + * by monitoring STAT.operating_mode signal + */ + + /* Wait uMCTL2 ready */ + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); + + /* Switch to DLL OFF mode */ + if ((config->c_reg.mstr & DDRCTRL_MSTR_DLL_OFF_MODE) != 0U) { + stm32mp1_ddr3_dll_off(priv); + } + + VERBOSE("DDR DQS training : "); + + /* + * 8. Disable Auto refresh and power down by setting + * - RFSHCTL3.dis_au_refresh = 1 + * - PWRCTL.powerdown_en = 0 + * - DFIMISC.dfiinit_complete_en = 0 + */ + stm32mp1_refresh_disable(priv->ctl); + + /* + * 9. Program PUBL PGCR to enable refresh during training + * and rank to train + * not done => keep the programed value in PGCR + */ + + /* + * 10. configure PUBL PIR register to specify which training step + * to run + * Warning : RVTRN is not supported by this PUBL + */ + stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_QSTRN); + + /* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training sequence */ + stm32mp1_ddrphy_idone_wait(priv->phy); + + /* + * 12. set back registers in step 8 to the orginal values if desidered + */ + stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, + config->c_reg.pwrctl); + + /* Enable uMCTL2 AXI port 0 */ + mmio_setbits_32((uint32_t)&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%x] pctrl_0 = 0x%x\n", + (uint32_t)&priv->ctl->pctrl_0, + mmio_read_32((uint32_t)&priv->ctl->pctrl_0)); + + /* Enable uMCTL2 AXI port 1 */ + mmio_setbits_32((uint32_t)&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%x] pctrl_1 = 0x%x\n", + (uint32_t)&priv->ctl->pctrl_1, + mmio_read_32((uint32_t)&priv->ctl->pctrl_1)); +} diff --git a/drivers/st/ddr/stm32mp1_ddr_helpers.c b/drivers/st/ddr/stm32mp1_ddr_helpers.c new file mode 100644 index 0000000..325c0b8 --- /dev/null +++ b/drivers/st/ddr/stm32mp1_ddr_helpers.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +void ddr_enable_clock(void) +{ + mmio_setbits_32(RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | + RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRPHYCEN | + RCC_DDRITFCR_DDRPHYCAPBEN | + RCC_DDRITFCR_DDRCAPBEN); +} diff --git a/drivers/st/ddr/stm32mp1_ram.c b/drivers/st/ddr/stm32mp1_ram.c new file mode 100644 index 0000000..6d515ec --- /dev/null +++ b/drivers/st/ddr/stm32mp1_ram.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DDR_PATTERN 0xAAAAAAAAU +#define DDR_ANTIPATTERN 0x55555555U + +static struct ddr_info ddr_priv_data; + +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) +{ + unsigned long ddrphy_clk, ddr_clk, mem_speed_hz; + + ddr_enable_clock(); + + ddrphy_clk = stm32mp1_clk_get_rate(DDRPHYC); + + VERBOSE("DDR: mem_speed (%d MHz), RCC %ld MHz\n", + mem_speed, ddrphy_clk / 1000U / 1000U); + + mem_speed_hz = (uint32_t)mem_speed * 1000U * 1000U; + + /* Max 10% frequency delta */ + if (ddrphy_clk > mem_speed_hz) { + ddr_clk = ddrphy_clk - mem_speed_hz; + } else { + ddr_clk = mem_speed_hz - ddrphy_clk; + } + if (ddr_clk > mem_speed_hz) { + ERROR("DDR expected freq %d MHz, current is %ld MHz\n", + mem_speed, ddrphy_clk / 1000U / 1000U); + return -1; + } + 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. + * resources.oreilly.com/examples/9781565923546/blob/master/Chapter6/ + * File: memtest.c - This source code belongs to Public Domain. + * Returns 0 if success, and address value else. + ******************************************************************************/ +static uint32_t ddr_test_data_bus(void) +{ + uint32_t pattern; + + for (pattern = 1U; pattern != 0U; pattern <<= 1) { + mmio_write_32(STM32MP1_DDR_BASE, pattern); + + if (mmio_read_32(STM32MP1_DDR_BASE) != pattern) { + return (uint32_t)STM32MP1_DDR_BASE; + } + } + + return 0; +} + +/******************************************************************************* + * This function tests the DDR address bus wiring. + * This is inspired from the Data Bus Test algorithm written by Michael Barr + * in "Programming Embedded Systems in C and C++" book. + * resources.oreilly.com/examples/9781565923546/blob/master/Chapter6/ + * File: memtest.c - This source code belongs to Public Domain. + * Returns 0 if success, and address value else. + ******************************************************************************/ +static uint32_t ddr_test_addr_bus(void) +{ + uint64_t addressmask = (ddr_priv_data.info.size - 1U); + uint64_t offset; + uint64_t testoffset = 0; + + /* Write the default pattern at each of the power-of-two offsets. */ + for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + offset <<= 1) { + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)offset, + DDR_PATTERN); + } + + /* Check for address bits stuck high. */ + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, + DDR_ANTIPATTERN); + + for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + offset <<= 1) { + if (mmio_read_32(STM32MP1_DDR_BASE + (uint32_t)offset) != + DDR_PATTERN) { + return (uint32_t)(STM32MP1_DDR_BASE + offset); + } + } + + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, DDR_PATTERN); + + /* Check for address bits stuck low or shorted. */ + for (testoffset = sizeof(uint32_t); (testoffset & addressmask) != 0U; + testoffset <<= 1) { + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, + DDR_ANTIPATTERN); + + if (mmio_read_32(STM32MP1_DDR_BASE) != DDR_PATTERN) { + return STM32MP1_DDR_BASE; + } + + for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + offset <<= 1) { + if ((mmio_read_32(STM32MP1_DDR_BASE + + (uint32_t)offset) != DDR_PATTERN) && + (offset != testoffset)) { + return (uint32_t)(STM32MP1_DDR_BASE + offset); + } + } + + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, + DDR_PATTERN); + } + + return 0; +} + +/******************************************************************************* + * This function checks the DDR size. It has to be run with Data Cache off. + * This test is run before data have been put in DDR, and is only done for + * cold boot. The DDR data can then be overwritten, and it is not useful to + * restore its content. + * Returns DDR computed size. + ******************************************************************************/ +static uint32_t ddr_check_size(void) +{ + uint32_t offset = sizeof(uint32_t); + + mmio_write_32(STM32MP1_DDR_BASE, DDR_PATTERN); + + while (offset < STM32MP1_DDR_MAX_SIZE) { + mmio_write_32(STM32MP1_DDR_BASE + offset, DDR_ANTIPATTERN); + dsb(); + + if (mmio_read_32(STM32MP1_DDR_BASE) != DDR_PATTERN) { + break; + } + + offset <<= 1; + } + + INFO("Memory size = 0x%x (%d MB)\n", offset, offset / (1024U * 1024U)); + + return offset; +} + +static int stm32mp1_ddr_setup(void) +{ + struct ddr_info *priv = &ddr_priv_data; + int ret; + struct stm32mp1_ddr_config config; + int node, len; + uint32_t tamp_clk_off = 0, uret, idx; + void *fdt; + +#define PARAM(x, y) \ + { \ + .name = x, \ + .offset = offsetof(struct stm32mp1_ddr_config, y), \ + .size = sizeof(config.y) / sizeof(uint32_t) \ + } + +#define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x) +#define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x) + + const struct { + const char *name; /* Name in DT */ + const uint32_t offset; /* Offset in config struct */ + const uint32_t size; /* Size of parameters */ + } param[] = { + CTL_PARAM(reg), + CTL_PARAM(timing), + CTL_PARAM(map), + CTL_PARAM(perf), + PHY_PARAM(reg), + PHY_PARAM(timing), + PHY_PARAM(cal) + }; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + if (node < 0) { + ERROR("%s: Cannot read DDR node in DT\n", __func__); + return -EINVAL; + } + + config.info.speed = + (uint16_t)fdt_read_uint32_default(node, "st,mem-speed", + STM32MP1_DDR_SPEED_DFLT); + config.info.size = fdt_read_uint32_default(node, "st,mem-size", + STM32MP1_DDR_SIZE_DFLT); + config.info.name = fdt_getprop(fdt, node, "st,mem-name", &len); + if (config.info.name == NULL) { + VERBOSE("%s: no st,mem-name\n", __func__); + return -EINVAL; + } + INFO("RAM: %s\n", config.info.name); + + for (idx = 0; idx < ARRAY_SIZE(param); idx++) { + ret = fdt_read_uint32_array(node, param[idx].name, + (void *)((uint32_t)&config + + param[idx].offset), + param[idx].size); + + VERBOSE("%s: %s[0x%x] = %d\n", __func__, + param[idx].name, param[idx].size, ret); + if (ret != 0) { + ERROR("%s: Cannot read %s\n", + __func__, param[idx].name); + return -EINVAL; + } + } + + if (!stm32mp1_clk_is_enabled(RTCAPB)) { + tamp_clk_off = 1; + if (stm32mp1_clk_enable(RTCAPB) != 0) { + return -EINVAL; + } + } + + if (tamp_clk_off != 0U) { + if (stm32mp1_clk_disable(RTCAPB) != 0) { + return -EINVAL; + } + } + + /* Disable axidcg clock gating during init */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_ddr_init(priv, &config); + + /* Enable axidcg clock gating */ + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + priv->info.size = config.info.size; + + VERBOSE("%s : ram size(%x, %x)\n", __func__, + (uint32_t)priv->info.base, (uint32_t)priv->info.size); + + dcsw_op_all(DC_OP_CISW); + write_sctlr(read_sctlr() & ~SCTLR_C_BIT); + + uret = ddr_test_data_bus(); + if (uret != 0U) { + ERROR("DDR data bus 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(); + } + + 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); + + return 0; +} + +int stm32mp1_ddr_probe(void) +{ + struct ddr_info *priv = &ddr_priv_data; + + VERBOSE("STM32MP DDR probe\n"); + + priv->ctl = (struct stm32mp1_ddrctl *)DDRCTRL_BASE; + priv->phy = (struct stm32mp1_ddrphy *)DDRPHYC_BASE; + priv->pwr = PWR_BASE; + priv->rcc = RCC_BASE; + + priv->info.base = STM32MP1_DDR_BASE; + priv->info.size = 0; + + return stm32mp1_ddr_setup(); +} diff --git a/include/drivers/st/stm32mp1_ddr.h b/include/drivers/st/stm32mp1_ddr.h new file mode 100644 index 0000000..0765664 --- /dev/null +++ b/include/drivers/st/stm32mp1_ddr.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef _STM32MP1_DDR_H +#define _STM32MP1_DDR_H + +#include + +#define DT_DDR_COMPAT "st,stm32mp1-ddr" + +struct stm32mp1_ddr_size { + uint64_t base; + uint64_t size; +}; + +/** + * struct ddr_info + * + * @dev: pointer for the device + * @info: UCLASS RAM information + * @ctl: DDR controleur base address + * @phy: DDR PHY base address + * @syscfg: syscfg base address + */ +struct ddr_info { + struct stm32mp1_ddr_size info; + struct stm32mp1_ddrctl *ctl; + struct stm32mp1_ddrphy *phy; + uintptr_t pwr; + uintptr_t rcc; +}; + +struct stm32mp1_ddrctrl_reg { + uint32_t mstr; + uint32_t mrctrl0; + uint32_t mrctrl1; + uint32_t derateen; + uint32_t derateint; + uint32_t pwrctl; + uint32_t pwrtmg; + uint32_t hwlpctl; + uint32_t rfshctl0; + uint32_t rfshctl3; + uint32_t crcparctl0; + uint32_t zqctl0; + uint32_t dfitmg0; + uint32_t dfitmg1; + uint32_t dfilpcfg0; + uint32_t dfiupd0; + uint32_t dfiupd1; + uint32_t dfiupd2; + uint32_t dfiphymstr; + uint32_t odtmap; + uint32_t dbg0; + uint32_t dbg1; + uint32_t dbgcmd; + uint32_t poisoncfg; + uint32_t pccfg; +}; + +struct stm32mp1_ddrctrl_timing { + uint32_t rfshtmg; + uint32_t dramtmg0; + uint32_t dramtmg1; + uint32_t dramtmg2; + uint32_t dramtmg3; + uint32_t dramtmg4; + uint32_t dramtmg5; + uint32_t dramtmg6; + uint32_t dramtmg7; + uint32_t dramtmg8; + uint32_t dramtmg14; + uint32_t odtcfg; +}; + +struct stm32mp1_ddrctrl_map { + uint32_t addrmap1; + uint32_t addrmap2; + uint32_t addrmap3; + uint32_t addrmap4; + uint32_t addrmap5; + uint32_t addrmap6; + uint32_t addrmap9; + uint32_t addrmap10; + uint32_t addrmap11; +}; + +struct stm32mp1_ddrctrl_perf { + uint32_t sched; + uint32_t sched1; + uint32_t perfhpr1; + uint32_t perflpr1; + uint32_t perfwr1; + uint32_t pcfgr_0; + uint32_t pcfgw_0; + uint32_t pcfgqos0_0; + uint32_t pcfgqos1_0; + uint32_t pcfgwqos0_0; + uint32_t pcfgwqos1_0; + uint32_t pcfgr_1; + uint32_t pcfgw_1; + uint32_t pcfgqos0_1; + uint32_t pcfgqos1_1; + uint32_t pcfgwqos0_1; + uint32_t pcfgwqos1_1; +}; + +struct stm32mp1_ddrphy_reg { + uint32_t pgcr; + uint32_t aciocr; + uint32_t dxccr; + uint32_t dsgcr; + uint32_t dcr; + uint32_t odtcr; + uint32_t zq0cr1; + uint32_t dx0gcr; + uint32_t dx1gcr; + uint32_t dx2gcr; + uint32_t dx3gcr; +}; + +struct stm32mp1_ddrphy_timing { + uint32_t ptr0; + uint32_t ptr1; + uint32_t ptr2; + uint32_t dtpr0; + uint32_t dtpr1; + uint32_t dtpr2; + uint32_t mr0; + uint32_t mr1; + uint32_t mr2; + uint32_t mr3; +}; + +struct stm32mp1_ddrphy_cal { + uint32_t dx0dllcr; + uint32_t dx0dqtr; + uint32_t dx0dqstr; + uint32_t dx1dllcr; + uint32_t dx1dqtr; + uint32_t dx1dqstr; + uint32_t dx2dllcr; + uint32_t dx2dqtr; + uint32_t dx2dqstr; + uint32_t dx3dllcr; + uint32_t dx3dqtr; + uint32_t dx3dqstr; +}; + +struct stm32mp1_ddr_info { + const char *name; + uint16_t speed; /* in MHZ */ + uint32_t size; /* Memory size in byte = col * row * width */ +}; + +struct stm32mp1_ddr_config { + struct stm32mp1_ddr_info info; + struct stm32mp1_ddrctrl_reg c_reg; + struct stm32mp1_ddrctrl_timing c_timing; + struct stm32mp1_ddrctrl_map c_map; + struct stm32mp1_ddrctrl_perf c_perf; + struct stm32mp1_ddrphy_reg p_reg; + struct stm32mp1_ddrphy_timing p_timing; + struct stm32mp1_ddrphy_cal p_cal; +}; + +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_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 new file mode 100644 index 0000000..298a080 --- /dev/null +++ b/include/drivers/st/stm32mp1_ddr_helpers.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_DDR_HELPERS_H__ +#define __STM32MP1_DDR_HELPERS_H__ + +void ddr_enable_clock(void); + +#endif /* __STM32MP1_DDR_HELPERS_H__ */ diff --git a/include/drivers/st/stm32mp1_ddr_regs.h b/include/drivers/st/stm32mp1_ddr_regs.h new file mode 100644 index 0000000..64ad965 --- /dev/null +++ b/include/drivers/st/stm32mp1_ddr_regs.h @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef _RAM_STM32MP1_DDR_REGS_H +#define _RAM_STM32MP1_DDR_REGS_H + +#include + +/* DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL) registers */ +struct stm32mp1_ddrctl { + uint32_t mstr ; /* 0x0 Master */ + uint32_t stat; /* 0x4 Operating Mode Status */ + uint8_t reserved008[0x10 - 0x8]; + uint32_t mrctrl0; /* 0x10 Control 0 */ + uint32_t mrctrl1; /* 0x14 Control 1 */ + uint32_t mrstat; /* 0x18 Status */ + uint32_t reserved01c; /* 0x1c */ + uint32_t derateen; /* 0x20 Temperature Derate Enable */ + uint32_t derateint; /* 0x24 Temperature Derate Interval */ + uint8_t reserved028[0x30 - 0x28]; + uint32_t pwrctl; /* 0x30 Low Power Control */ + uint32_t pwrtmg; /* 0x34 Low Power Timing */ + uint32_t hwlpctl; /* 0x38 Hardware Low Power Control */ + uint8_t reserved03c[0x50 - 0x3C]; + uint32_t rfshctl0; /* 0x50 Refresh Control 0 */ + uint32_t reserved054; /* 0x54 Refresh Control 1 */ + uint32_t reserved058; /* 0x58 Refresh Control 2 */ + uint32_t reserved05C; + uint32_t rfshctl3; /* 0x60 Refresh Control 0 */ + uint32_t rfshtmg; /* 0x64 Refresh Timing */ + uint8_t reserved068[0xc0 - 0x68]; + uint32_t crcparctl0; /* 0xc0 CRC Parity Control0 */ + uint32_t reserved0c4; /* 0xc4 CRC Parity Control1 */ + uint32_t reserved0c8; /* 0xc8 CRC Parity Control2 */ + uint32_t crcparstat; /* 0xcc CRC Parity Status */ + uint32_t init0; /* 0xd0 SDRAM Initialization 0 */ + uint32_t init1; /* 0xd4 SDRAM Initialization 1 */ + uint32_t init2; /* 0xd8 SDRAM Initialization 2 */ + uint32_t init3; /* 0xdc SDRAM Initialization 3 */ + uint32_t init4; /* 0xe0 SDRAM Initialization 4 */ + uint32_t init5; /* 0xe4 SDRAM Initialization 5 */ + uint32_t reserved0e8; + uint32_t reserved0ec; + uint32_t dimmctl; /* 0xf0 DIMM Control */ + uint8_t reserved0f4[0x100 - 0xf4]; + uint32_t dramtmg0; /* 0x100 SDRAM Timing 0 */ + uint32_t dramtmg1; /* 0x104 SDRAM Timing 1 */ + uint32_t dramtmg2; /* 0x108 SDRAM Timing 2 */ + uint32_t dramtmg3; /* 0x10c SDRAM Timing 3 */ + uint32_t dramtmg4; /* 0x110 SDRAM Timing 4 */ + uint32_t dramtmg5; /* 0x114 SDRAM Timing 5 */ + uint32_t dramtmg6; /* 0x118 SDRAM Timing 6 */ + uint32_t dramtmg7; /* 0x11c SDRAM Timing 7 */ + uint32_t dramtmg8; /* 0x120 SDRAM Timing 8 */ + uint8_t reserved124[0x138 - 0x124]; + uint32_t dramtmg14; /* 0x138 SDRAM Timing 14 */ + uint32_t dramtmg15; /* 0x13C SDRAM Timing 15 */ + uint8_t reserved140[0x180 - 0x140]; + uint32_t zqctl0; /* 0x180 ZQ Control 0 */ + uint32_t zqctl1; /* 0x184 ZQ Control 1 */ + uint32_t zqctl2; /* 0x188 ZQ Control 2 */ + uint32_t zqstat; /* 0x18c ZQ Status */ + uint32_t dfitmg0; /* 0x190 DFI Timing 0 */ + uint32_t dfitmg1; /* 0x194 DFI Timing 1 */ + uint32_t dfilpcfg0; /* 0x198 DFI Low Power Configuration 0 */ + uint32_t reserved19c; + uint32_t dfiupd0; /* 0x1a0 DFI Update 0 */ + uint32_t dfiupd1; /* 0x1a4 DFI Update 1 */ + uint32_t dfiupd2; /* 0x1a8 DFI Update 2 */ + uint32_t reserved1ac; + uint32_t dfimisc; /* 0x1b0 DFI Miscellaneous Control */ + uint8_t reserved1b4[0x1bc - 0x1b4]; + uint32_t dfistat; /* 0x1bc DFI Miscellaneous Control */ + uint8_t reserved1c0[0x1c4 - 0x1c0]; + uint32_t dfiphymstr; /* 0x1c4 DFI PHY Master interface */ + uint8_t reserved1c8[0x204 - 0x1c8]; + uint32_t addrmap1; /* 0x204 Address Map 1 */ + uint32_t addrmap2; /* 0x208 Address Map 2 */ + uint32_t addrmap3; /* 0x20c Address Map 3 */ + uint32_t addrmap4; /* 0x210 Address Map 4 */ + uint32_t addrmap5; /* 0x214 Address Map 5 */ + uint32_t addrmap6; /* 0x218 Address Map 6 */ + uint8_t reserved21c[0x224 - 0x21c]; + uint32_t addrmap9; /* 0x224 Address Map 9 */ + uint32_t addrmap10; /* 0x228 Address Map 10 */ + uint32_t addrmap11; /* 0x22C Address Map 11 */ + uint8_t reserved230[0x240 - 0x230]; + uint32_t odtcfg; /* 0x240 ODT Configuration */ + uint32_t odtmap; /* 0x244 ODT/Rank Map */ + uint8_t reserved248[0x250 - 0x248]; + uint32_t sched; /* 0x250 Scheduler Control */ + uint32_t sched1; /* 0x254 Scheduler Control 1 */ + uint32_t reserved258; + uint32_t perfhpr1; /* 0x25c High Priority Read CAM 1 */ + uint32_t reserved260; + uint32_t perflpr1; /* 0x264 Low Priority Read CAM 1 */ + uint32_t reserved268; + uint32_t perfwr1; /* 0x26c Write CAM 1 */ + uint8_t reserved27c[0x300 - 0x270]; + uint32_t dbg0; /* 0x300 Debug 0 */ + uint32_t dbg1; /* 0x304 Debug 1 */ + uint32_t dbgcam; /* 0x308 CAM Debug */ + uint32_t dbgcmd; /* 0x30c Command Debug */ + uint32_t dbgstat; /* 0x310 Status Debug */ + uint8_t reserved314[0x320 - 0x314]; + uint32_t swctl; /* 0x320 Software Programming Control Enable */ + uint32_t swstat; /* 0x324 Software Programming Control Status */ + uint8_t reserved328[0x36c - 0x328]; + uint32_t poisoncfg; /* 0x36c AXI Poison Configuration Register */ + uint32_t poisonstat; /* 0x370 AXI Poison Status Register */ + uint8_t reserved374[0x3fc - 0x374]; + + /* Multi Port registers */ + uint32_t pstat; /* 0x3fc Port Status */ + uint32_t pccfg; /* 0x400 Port Common Configuration */ + + /* PORT 0 */ + uint32_t pcfgr_0; /* 0x404 Configuration Read */ + uint32_t pcfgw_0; /* 0x408 Configuration Write */ + uint8_t reserved40c[0x490 - 0x40c]; + uint32_t pctrl_0; /* 0x490 Port Control Register */ + uint32_t pcfgqos0_0; /* 0x494 Read QoS Configuration 0 */ + uint32_t pcfgqos1_0; /* 0x498 Read QoS Configuration 1 */ + uint32_t pcfgwqos0_0; /* 0x49c Write QoS Configuration 0 */ + uint32_t pcfgwqos1_0; /* 0x4a0 Write QoS Configuration 1 */ + uint8_t reserved4a4[0x4b4 - 0x4a4]; + + /* PORT 1 */ + uint32_t pcfgr_1; /* 0x4b4 Configuration Read */ + uint32_t pcfgw_1; /* 0x4b8 Configuration Write */ + uint8_t reserved4bc[0x540 - 0x4bc]; + uint32_t pctrl_1; /* 0x540 Port 2 Control Register */ + uint32_t pcfgqos0_1; /* 0x544 Read QoS Configuration 0 */ + uint32_t pcfgqos1_1; /* 0x548 Read QoS Configuration 1 */ + uint32_t pcfgwqos0_1; /* 0x54c Write QoS Configuration 0 */ + uint32_t pcfgwqos1_1; /* 0x550 Write QoS Configuration 1 */ +} __packed; + +/* DDR Physical Interface Control (DDRPHYC) registers*/ +struct stm32mp1_ddrphy { + uint32_t ridr; /* 0x00 R Revision Identification */ + uint32_t pir; /* 0x04 R/W PHY Initialization */ + uint32_t pgcr; /* 0x08 R/W PHY General Configuration */ + uint32_t pgsr; /* 0x0C PHY General Status */ + uint32_t dllgcr; /* 0x10 R/W DLL General Control */ + uint32_t acdllcr; /* 0x14 R/W AC DLL Control */ + uint32_t ptr0; /* 0x18 R/W PHY Timing 0 */ + uint32_t ptr1; /* 0x1C R/W PHY Timing 1 */ + uint32_t ptr2; /* 0x20 R/W PHY Timing 2 */ + uint32_t aciocr; /* 0x24 AC I/O Configuration */ + uint32_t dxccr; /* 0x28 DATX8 Common Configuration */ + uint32_t dsgcr; /* 0x2C DDR System General Configuration */ + uint32_t dcr; /* 0x30 DRAM Configuration */ + uint32_t dtpr0; /* 0x34 DRAM Timing Parameters0 */ + uint32_t dtpr1; /* 0x38 DRAM Timing Parameters1 */ + uint32_t dtpr2; /* 0x3C DRAM Timing Parameters2 */ + uint32_t mr0; /* 0x40 Mode 0 */ + uint32_t mr1; /* 0x44 Mode 1 */ + uint32_t mr2; /* 0x48 Mode 2 */ + uint32_t mr3; /* 0x4C Mode 3 */ + uint32_t odtcr; /* 0x50 ODT Configuration */ + uint32_t dtar; /* 0x54 data training address */ + uint32_t dtdr0; /* 0x58 */ + uint32_t dtdr1; /* 0x5c */ + uint8_t res1[0x0c0 - 0x060]; /* 0x60 */ + uint32_t dcuar; /* 0xc0 Address */ + uint32_t dcudr; /* 0xc4 DCU Data */ + uint32_t dcurr; /* 0xc8 DCU Run */ + uint32_t dculr; /* 0xcc DCU Loop */ + uint32_t dcugcr; /* 0xd0 DCU General Configuration */ + uint32_t dcutpr; /* 0xd4 DCU Timing Parameters */ + uint32_t dcusr0; /* 0xd8 DCU Status 0 */ + uint32_t dcusr1; /* 0xdc DCU Status 1 */ + uint8_t res2[0x100 - 0xe0]; /* 0xe0 */ + uint32_t bistrr; /* 0x100 BIST Run */ + uint32_t bistmskr0; /* 0x104 BIST Mask 0 */ + uint32_t bistmskr1; /* 0x108 BIST Mask 0 */ + uint32_t bistwcr; /* 0x10c BIST Word Count */ + uint32_t bistlsr; /* 0x110 BIST LFSR Seed */ + uint32_t bistar0; /* 0x114 BIST Address 0 */ + uint32_t bistar1; /* 0x118 BIST Address 1 */ + uint32_t bistar2; /* 0x11c BIST Address 2 */ + uint32_t bistupdr; /* 0x120 BIST User Data Pattern */ + uint32_t bistgsr; /* 0x124 BIST General Status */ + uint32_t bistwer; /* 0x128 BIST Word Error */ + uint32_t bistber0; /* 0x12c BIST Bit Error 0 */ + uint32_t bistber1; /* 0x130 BIST Bit Error 1 */ + uint32_t bistber2; /* 0x134 BIST Bit Error 2 */ + uint32_t bistwcsr; /* 0x138 BIST Word Count Status */ + uint32_t bistfwr0; /* 0x13c BIST Fail Word 0 */ + uint32_t bistfwr1; /* 0x140 BIST Fail Word 1 */ + uint8_t res3[0x178 - 0x144]; /* 0x144 */ + uint32_t gpr0; /* 0x178 General Purpose 0 (GPR0) */ + uint32_t gpr1; /* 0x17C General Purpose 1 (GPR1) */ + uint32_t zq0cr0; /* 0x180 zq 0 control 0 */ + uint32_t zq0cr1; /* 0x184 zq 0 control 1 */ + uint32_t zq0sr0; /* 0x188 zq 0 status 0 */ + uint32_t zq0sr1; /* 0x18C zq 0 status 1 */ + uint8_t res4[0x1C0 - 0x190]; /* 0x190 */ + uint32_t dx0gcr; /* 0x1c0 Byte lane 0 General Configuration */ + uint32_t dx0gsr0; /* 0x1c4 Byte lane 0 General Status 0 */ + uint32_t dx0gsr1; /* 0x1c8 Byte lane 0 General Status 1 */ + uint32_t dx0dllcr; /* 0x1cc Byte lane 0 DLL Control */ + uint32_t dx0dqtr; /* 0x1d0 Byte lane 0 DQ Timing */ + uint32_t dx0dqstr; /* 0x1d4 Byte lane 0 DQS Timing */ + uint8_t res5[0x200 - 0x1d8]; /* 0x1d8 */ + uint32_t dx1gcr; /* 0x200 Byte lane 1 General Configuration */ + uint32_t dx1gsr0; /* 0x204 Byte lane 1 General Status 0 */ + uint32_t dx1gsr1; /* 0x208 Byte lane 1 General Status 1 */ + uint32_t dx1dllcr; /* 0x20c Byte lane 1 DLL Control */ + uint32_t dx1dqtr; /* 0x210 Byte lane 1 DQ Timing */ + uint32_t dx1dqstr; /* 0x214 Byte lane 1 QS Timing */ + uint8_t res6[0x240 - 0x218]; /* 0x218 */ + uint32_t dx2gcr; /* 0x240 Byte lane 2 General Configuration */ + uint32_t dx2gsr0; /* 0x244 Byte lane 2 General Status 0 */ + uint32_t dx2gsr1; /* 0x248 Byte lane 2 General Status 1 */ + uint32_t dx2dllcr; /* 0x24c Byte lane 2 DLL Control */ + uint32_t dx2dqtr; /* 0x250 Byte lane 2 DQ Timing */ + uint32_t dx2dqstr; /* 0x254 Byte lane 2 QS Timing */ + uint8_t res7[0x280 - 0x258]; /* 0x258 */ + uint32_t dx3gcr; /* 0x280 Byte lane 3 General Configuration */ + uint32_t dx3gsr0; /* 0x284 Byte lane 3 General Status 0 */ + uint32_t dx3gsr1; /* 0x288 Byte lane 3 General Status 1 */ + uint32_t dx3dllcr; /* 0x28c Byte lane 3 DLL Control */ + uint32_t dx3dqtr; /* 0x290 Byte lane 3 DQ Timing */ + uint32_t dx3dqstr; /* 0x294 Byte lane 3 QS Timing */ +} __packed; + +/* DDR Controller registers offsets */ +#define DDRCTRL_MSTR 0x000 +#define DDRCTRL_STAT 0x004 +#define DDRCTRL_MRCTRL0 0x010 +#define DDRCTRL_MRSTAT 0x018 +#define DDRCTRL_PWRCTL 0x030 +#define DDRCTRL_PWRTMG 0x034 +#define DDRCTRL_HWLPCTL 0x038 +#define DDRCTRL_RFSHCTL3 0x060 +#define DDRCTRL_RFSHTMG 0x064 +#define DDRCTRL_INIT0 0x0D0 +#define DDRCTRL_DFIMISC 0x1B0 +#define DDRCTRL_DBG1 0x304 +#define DDRCTRL_DBGCAM 0x308 +#define DDRCTRL_DBGCMD 0x30C +#define DDRCTRL_DBGSTAT 0x310 +#define DDRCTRL_SWCTL 0x320 +#define DDRCTRL_SWSTAT 0x324 +#define DDRCTRL_PCTRL_0 0x490 +#define DDRCTRL_PCTRL_1 0x540 + +/* DDR Controller Register fields */ +#define DDRCTRL_MSTR_DDR3 BIT(0) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK(13, 12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_FULL 0 +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF BIT(12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER BIT(13) +#define DDRCTRL_MSTR_DLL_OFF_MODE BIT(15) + +#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0) +#define DDRCTRL_STAT_OPERATING_MODE_NORMAL BIT(0) +#define DDRCTRL_STAT_OPERATING_MODE_SR (BIT(0) | BIT(1)) +#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) +#define DDRCTRL_STAT_SELFREF_TYPE_ASR (BIT(4) | BIT(5)) +#define DDRCTRL_STAT_SELFREF_TYPE_SR BIT(5) + +#define DDRCTRL_MRCTRL0_MR_TYPE_WRITE U(0) +/* Only one rank supported */ +#define DDRCTRL_MRCTRL0_MR_RANK_SHIFT 4 +#define DDRCTRL_MRCTRL0_MR_RANK_ALL \ + (0x1U << DDRCTRL_MRCTRL0_MR_RANK_SHIFT) +#define DDRCTRL_MRCTRL0_MR_ADDR_SHIFT 12 +#define DDRCTRL_MRCTRL0_MR_ADDR_MASK GENMASK(15, 12) +#define DDRCTRL_MRCTRL0_MR_WR BIT(31) + +#define DDRCTRL_MRSTAT_MR_WR_BUSY BIT(0) + +#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) +#define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) +#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) + +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(19, 12) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) + +#define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) + +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) + +#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) +#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT 16 + +#define DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK GENMASK(31, 30) +#define DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL BIT(30) + +#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) + +#define DDRCTRL_DBG1_DIS_HIF BIT(1) + +#define DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY BIT(29) +#define DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY BIT(28) +#define DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY BIT(26) +#define DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH GENMASK(12, 8) +#define DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH GENMASK(4, 0) +#define DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY \ + (DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY | \ + DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY) +#define DDRCTRL_DBGCAM_DBG_Q_DEPTH \ + (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \ + DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH | \ + DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH) + +#define DDRCTRL_DBGCMD_RANK0_REFRESH BIT(0) + +#define DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY BIT(0) + +#define DDRCTRL_SWCTL_SW_DONE BIT(0) + +#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0) + +#define DDRCTRL_PCTRL_N_PORT_EN BIT(0) + +/* DDR PHY registers offsets */ +#define DDRPHYC_PIR 0x004 +#define DDRPHYC_PGCR 0x008 +#define DDRPHYC_PGSR 0x00C +#define DDRPHYC_DLLGCR 0x010 +#define DDRPHYC_ACDLLCR 0x014 +#define DDRPHYC_PTR0 0x018 +#define DDRPHYC_ACIOCR 0x024 +#define DDRPHYC_DXCCR 0x028 +#define DDRPHYC_DSGCR 0x02C +#define DDRPHYC_ZQ0CR0 0x180 +#define DDRPHYC_DX0GCR 0x1C0 +#define DDRPHYC_DX0DLLCR 0x1CC +#define DDRPHYC_DX1GCR 0x200 +#define DDRPHYC_DX1DLLCR 0x20C +#define DDRPHYC_DX2GCR 0x240 +#define DDRPHYC_DX2DLLCR 0x24C +#define DDRPHYC_DX3GCR 0x280 +#define DDRPHYC_DX3DLLCR 0x28C + +/* DDR PHY Register fields */ +#define DDRPHYC_PIR_INIT BIT(0) +#define DDRPHYC_PIR_DLLSRST BIT(1) +#define DDRPHYC_PIR_DLLLOCK BIT(2) +#define DDRPHYC_PIR_ZCAL BIT(3) +#define DDRPHYC_PIR_ITMSRST BIT(4) +#define DDRPHYC_PIR_DRAMRST BIT(5) +#define DDRPHYC_PIR_DRAMINIT BIT(6) +#define DDRPHYC_PIR_QSTRN BIT(7) +#define DDRPHYC_PIR_ICPC BIT(16) +#define DDRPHYC_PIR_ZCALBYP BIT(30) +#define DDRPHYC_PIR_INITSTEPS_MASK GENMASK(31, 7) + +#define DDRPHYC_PGCR_DFTCMP BIT(2) +#define DDRPHYC_PGCR_PDDISDX BIT(24) +#define DDRPHYC_PGCR_RFSHDT_MASK GENMASK(28, 25) + +#define DDRPHYC_PGSR_IDONE BIT(0) +#define DDRPHYC_PGSR_DTERR BIT(5) +#define DDRPHYC_PGSR_DTIERR BIT(6) +#define DDRPHYC_PGSR_DFTERR BIT(7) +#define DDRPHYC_PGSR_RVERR BIT(8) +#define DDRPHYC_PGSR_RVEIRR BIT(9) + +#define DDRPHYC_DLLGCR_BPS200 BIT(23) + +#define DDRPHYC_ACDLLCR_DLLDIS BIT(31) + +#define DDRPHYC_PTR0_TDLLSRST_OFFSET 0 +#define DDRPHYC_PTR0_TDLLSRST_MASK GENMASK(5, 0) +#define DDRPHYC_PTR0_TDLLLOCK_OFFSET 6 +#define DDRPHYC_PTR0_TDLLLOCK_MASK GENMASK(17, 6) +#define DDRPHYC_PTR0_TITMSRST_OFFSET 18 +#define DDRPHYC_PTR0_TITMSRST_MASK GENMASK(21, 18) + +#define DDRPHYC_ACIOCR_ACPDD BIT(3) +#define DDRPHYC_ACIOCR_ACPDR BIT(4) +#define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK(10, 8) +#define DDRPHYC_ACIOCR_CKPDD_0 BIT(8) +#define DDRPHYC_ACIOCR_CKPDR_MASK GENMASK(13, 11) +#define DDRPHYC_ACIOCR_CKPDR_0 BIT(11) +#define DDRPHYC_ACIOCR_CSPDD_MASK GENMASK(21, 18) +#define DDRPHYC_ACIOCR_CSPDD_0 BIT(18) +#define DDRPHYC_ACIOCR_RSTPDD BIT(27) +#define DDRPHYC_ACIOCR_RSTPDR BIT(28) + +#define DDRPHYC_DXCCR_DXPDD BIT(2) +#define DDRPHYC_DXCCR_DXPDR BIT(3) + +#define DDRPHYC_DSGCR_CKEPDD_MASK GENMASK(19, 16) +#define DDRPHYC_DSGCR_CKEPDD_0 BIT(16) +#define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK(23, 20) +#define DDRPHYC_DSGCR_ODTPDD_0 BIT(20) +#define DDRPHYC_DSGCR_NL2PD BIT(24) + +#define DDRPHYC_ZQ0CRN_ZDATA_MASK GENMASK(27, 0) +#define DDRPHYC_ZQ0CRN_ZDATA_SHIFT 0 +#define DDRPHYC_ZQ0CRN_ZDEN BIT(28) +#define DDRPHYC_ZQ0CRN_ZQPD BIT(31) + +#define DDRPHYC_DXNGCR_DXEN BIT(0) + +#define DDRPHYC_DXNDLLCR_DLLSRST BIT(30) +#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) +#define DDRPHYC_DXNDLLCR_SDPHASE_MASK GENMASK(17, 14) +#define DDRPHYC_DXNDLLCR_SDPHASE_SHIFT 14 + +void ddr_enable_clock(void); + +#endif /* _RAM_STM32MP1_DDR_REGS_H */ diff --git a/include/drivers/st/stm32mp1_ram.h b/include/drivers/st/stm32mp1_ram.h new file mode 100644 index 0000000..af96177 --- /dev/null +++ b/include/drivers/st/stm32mp1_ram.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _STM32MP1_RAM_H +#define _STM32MP1_RAM_H + +int stm32mp1_ddr_probe(void); + +#endif /* _STM32MP1_RAM_H */ diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index 84484e1..9f2d8bd 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -35,10 +36,18 @@ void bl2_platform_setup(void) { + int ret; + if (dt_check_pmic()) { initialize_pmic(); } + ret = stm32mp1_ddr_probe(); + if (ret < 0) { + ERROR("Invalid DDR init: error %d\n", ret); + panic(); + } + INFO("BL2 runs SP_MIN setup\n"); } @@ -146,5 +155,7 @@ ERROR("Cannot save boot interface\n"); } + stm32mp1_arch_security_setup(); + stm32mp1_io_setup(); } diff --git a/plat/st/stm32mp1/include/stm32mp1_dt.h b/plat/st/stm32mp1/include/stm32mp1_dt.h index 1b1024a..58e10d1 100644 --- a/plat/st/stm32mp1/include/stm32mp1_dt.h +++ b/plat/st/stm32mp1/include/stm32mp1_dt.h @@ -35,6 +35,7 @@ int dt_get_node(struct dt_node_info *info, int offset, const char *compat); int dt_get_stdout_uart_info(struct dt_node_info *info); int dt_get_stdout_node_offset(void); +uint32_t dt_get_ddr_size(void); const char *dt_get_board_model(void); #endif /* __STM32MP1_DT_H__ */ diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h index 6a0449e..41c46e8 100644 --- a/plat/st/stm32mp1/include/stm32mp1_private.h +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -10,6 +10,8 @@ void stm32mp1_io_setup(void); void configure_mmu(void); +void stm32mp1_arch_security_setup(void); + void stm32mp1_save_boot_ctx_address(uintptr_t address); uintptr_t stm32mp1_get_boot_ctx_address(void); diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index bd74d0f..1609213 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -37,10 +37,12 @@ PLAT_BL_COMMON_SOURCES += lib/cpus/aarch32/cortex_a7.S PLAT_BL_COMMON_SOURCES += ${LIBFDT_SRCS} \ + drivers/arm/tzc/tzc400.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ drivers/st/clk/stm32mp1_clk.c \ drivers/st/clk/stm32mp1_clkfunc.c \ + drivers/st/ddr/stm32mp1_ddr_helpers.c \ drivers/st/gpio/stm32_gpio.c \ drivers/st/pmic/stm32_i2c.c \ drivers/st/pmic/stm32mp1_pmic.c \ @@ -48,13 +50,17 @@ drivers/st/reset/stm32mp1_reset.c \ plat/st/stm32mp1/stm32mp1_context.c \ plat/st/stm32mp1/stm32mp1_dt.c \ - plat/st/stm32mp1/stm32mp1_helper.S + plat/st/stm32mp1/stm32mp1_helper.S \ + plat/st/stm32mp1/stm32mp1_security.c BL2_SOURCES += drivers/io/io_dummy.c \ drivers/io/io_storage.c \ plat/st/stm32mp1/bl2_io_storage.c \ plat/st/stm32mp1/bl2_plat_setup.c +BL2_SOURCES += drivers/st/ddr/stm32mp1_ddr.c \ + drivers/st/ddr/stm32mp1_ram.c + BL2_SOURCES += common/desc_image_load.c \ plat/st/stm32mp1/plat_bl2_mem_params_desc.c \ plat/st/stm32mp1/plat_image_load.c diff --git a/plat/st/stm32mp1/stm32mp1_common.c b/plat/st/stm32mp1/stm32mp1_common.c index 9f1126b..e2f90d2 100644 --- a/plat/st/stm32mp1/stm32mp1_common.c +++ b/plat/st/stm32mp1/stm32mp1_common.c @@ -36,10 +36,18 @@ MT_SECURE | \ MT_EXECUTE_NEVER) +#define MAP_DDR MAP_REGION_FLAT(STM32MP1_DDR_BASE, \ + STM32MP1_DDR_MAX_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + static const mmap_region_t stm32mp1_mmap[] = { MAP_SRAM, MAP_DEVICE1, MAP_DEVICE2, + MAP_DDR, {0} }; diff --git a/plat/st/stm32mp1/stm32mp1_dt.c b/plat/st/stm32mp1/stm32mp1_dt.c index 7caf655..bde968a 100644 --- a/plat/st/stm32mp1/stm32mp1_dt.c +++ b/plat/st/stm32mp1/stm32mp1_dt.c @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #define DT_GPIO_BANK_SHIFT 12 #define DT_GPIO_BANK_MASK 0x1F000U @@ -441,6 +443,24 @@ } /******************************************************************************* + * This function gets DDR size information from the DT. + * Returns value in bytes if success, and STM32MP1_DDR_SIZE_DFLT else. + ******************************************************************************/ +uint32_t dt_get_ddr_size(void) +{ + int node; + + node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + if (node < 0) { + INFO("%s: Cannot read DDR node in DT\n", __func__); + return STM32MP1_DDR_SIZE_DFLT; + } + + return fdt_read_uint32_default(node, "st,mem-size", + STM32MP1_DDR_SIZE_DFLT); +} + +/******************************************************************************* * This function retrieves board model from DT * Returns string taken from model node, NULL otherwise ******************************************************************************/ diff --git a/plat/st/stm32mp1/stm32mp1_security.c b/plat/st/stm32mp1/stm32mp1_security.c new file mode 100644 index 0000000..0cdf305 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_security.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_def.h" + +/******************************************************************************* + * Initialize the TrustZone Controller. + * Early initialization create only one region with full access to secure. + * This setting is used before and during DDR initialization. + ******************************************************************************/ +static void early_init_tzc400(void) +{ + uint32_t rstsr, rst_standby; + + rstsr = mmio_read_32(RCC_BASE + RCC_MP_RSTSCLRR); + + /* No warning if return from (C)STANDBY */ + rst_standby = rstsr & + (RCC_MP_RSTSCLRR_STDBYRSTF | RCC_MP_RSTSCLRR_CSTDBYRSTF); + + if (stm32mp1_clk_is_enabled(TZC1) && (rst_standby == 0U)) { + WARN("TZC400 port 1 clock already enable\n"); + } + + if (stm32mp1_clk_is_enabled(TZC2) && (rst_standby == 0U)) { + WARN("TZC400 port 2 clock already enable\n"); + } + + if (stm32mp1_clk_enable(TZC1) != 0) { + ERROR("Cannot enable TZC1 clock\n"); + panic(); + } + if (stm32mp1_clk_enable(TZC2) != 0) { + ERROR("Cannot enable TZC2 clock\n"); + panic(); + } + + tzc400_init(STM32MP1_TZC_BASE); + + tzc400_disable_filters(); + + /* + * Region 1 set to cover Non-Secure DRAM at 0x8000_0000. Apply the + * same configuration to all filters in the TZC. + */ + tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 1, + STM32MP1_DDR_BASE, + STM32MP1_DDR_BASE + + (STM32MP1_DDR_MAX_SIZE - 1U), + TZC_REGION_S_RDWR, + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID)); + + /* Raise an exception if a NS device tries to access secure memory */ + tzc400_set_action(TZC_ACTION_ERR); + + tzc400_enable_filters(); +} + +/******************************************************************************* + * Initialize the secure environment. At this moment only the TrustZone + * Controller is initialized. + ******************************************************************************/ +void stm32mp1_arch_security_setup(void) +{ + early_init_tzc400(); +}