From 0bc28b7cb833d6b16ad614d7e25d448a7b0297df Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 24 May 2018 00:17:30 +0800 Subject: ddr: altera: stratix10: Add DDR support for Stratix10 SoC Add DDR support for Stratix SoC Signed-off-by: Chin Liang See Signed-off-by: Ley Foon Tan --- drivers/ddr/altera/Makefile | 1 + drivers/ddr/altera/sdram_s10.c | 388 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 drivers/ddr/altera/sdram_s10.c (limited to 'drivers/ddr') diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile index f05314a373d..3615b617ecc 100644 --- a/drivers/ddr/altera/Makefile +++ b/drivers/ddr/altera/Makefile @@ -9,4 +9,5 @@ ifdef CONFIG_ALTERA_SDRAM obj-$(CONFIG_TARGET_SOCFPGA_GEN5) += sdram_gen5.o sequencer.o obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += sdram_arria10.o +obj-$(CONFIG_TARGET_SOCFPGA_STRATIX10) += sdram_s10.o endif diff --git a/drivers/ddr/altera/sdram_s10.c b/drivers/ddr/altera/sdram_s10.c new file mode 100644 index 00000000000..48f4f47b14b --- /dev/null +++ b/drivers/ddr/altera/sdram_s10.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Intel Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct socfpga_system_manager *sysmgr_regs = + (void *)SOCFPGA_SYSMGR_ADDRESS; + +#define DDR_CONFIG(A, B, C, R) (((A) << 24) | ((B) << 16) | ((C) << 8) | (R)) + +/* The followring are the supported configurations */ +u32 ddr_config[] = { + /* DDR_CONFIG(Address order,Bank,Column,Row) */ + /* List for DDR3 or LPDDR3 (pinout order > chip, row, bank, column) */ + DDR_CONFIG(0, 3, 10, 12), + DDR_CONFIG(0, 3, 9, 13), + DDR_CONFIG(0, 3, 10, 13), + DDR_CONFIG(0, 3, 9, 14), + DDR_CONFIG(0, 3, 10, 14), + DDR_CONFIG(0, 3, 10, 15), + DDR_CONFIG(0, 3, 11, 14), + DDR_CONFIG(0, 3, 11, 15), + DDR_CONFIG(0, 3, 10, 16), + DDR_CONFIG(0, 3, 11, 16), + DDR_CONFIG(0, 3, 12, 15), /* 0xa */ + /* List for DDR4 only (pinout order > chip, bank, row, column) */ + DDR_CONFIG(1, 3, 10, 14), + DDR_CONFIG(1, 4, 10, 14), + DDR_CONFIG(1, 3, 10, 15), + DDR_CONFIG(1, 4, 10, 15), + DDR_CONFIG(1, 3, 10, 16), + DDR_CONFIG(1, 4, 10, 16), + DDR_CONFIG(1, 3, 10, 17), + DDR_CONFIG(1, 4, 10, 17), +}; + +static u32 hmc_readl(u32 reg) +{ + return readl(((void __iomem *)SOCFPGA_HMC_MMR_IO48_ADDRESS + (reg))); +} + +static u32 hmc_ecc_readl(u32 reg) +{ + return readl((void __iomem *)SOCFPGA_SDR_ADDRESS + (reg)); +} + +static u32 hmc_ecc_writel(u32 data, u32 reg) +{ + return writel(data, (void __iomem *)SOCFPGA_SDR_ADDRESS + (reg)); +} + +static u32 ddr_sch_writel(u32 data, u32 reg) +{ + return writel(data, + (void __iomem *)SOCFPGA_SDR_SCHEDULER_ADDRESS + (reg)); +} + +int match_ddr_conf(u32 ddr_conf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ddr_config); i++) { + if (ddr_conf == ddr_config[i]) + return i; + } + return 0; +} + +static int emif_clear(void) +{ + hmc_ecc_writel(0, RSTHANDSHAKECTRL); + + return wait_for_bit_le32((const void *)(SOCFPGA_SDR_ADDRESS + + RSTHANDSHAKESTAT), + DDR_HMC_RSTHANDSHAKE_MASK, + false, 1000, false); +} + +static int emif_reset(void) +{ + u32 c2s, s2c, ret; + + c2s = hmc_ecc_readl(RSTHANDSHAKECTRL) & DDR_HMC_RSTHANDSHAKE_MASK; + s2c = hmc_ecc_readl(RSTHANDSHAKESTAT) & DDR_HMC_RSTHANDSHAKE_MASK; + + debug("DDR: c2s=%08x s2c=%08x nr0=%08x nr1=%08x nr2=%08x dst=%08x\n", + c2s, s2c, hmc_readl(NIOSRESERVED0), hmc_readl(NIOSRESERVED1), + hmc_readl(NIOSRESERVED2), hmc_readl(DRAMSTS)); + + if (s2c && emif_clear()) { + printf("DDR: emif_clear() failed\n"); + return -1; + } + + debug("DDR: Triggerring emif reset\n"); + hmc_ecc_writel(DDR_HMC_CORE2SEQ_INT_REQ, RSTHANDSHAKECTRL); + + /* if seq2core[3] = 0, we are good */ + ret = wait_for_bit_le32((const void *)(SOCFPGA_SDR_ADDRESS + + RSTHANDSHAKESTAT), + DDR_HMC_SEQ2CORE_INT_RESP_MASK, + false, 1000, false); + if (ret) { + printf("DDR: failed to get ack from EMIF\n"); + return ret; + } + + ret = emif_clear(); + if (ret) { + printf("DDR: emif_clear() failed\n"); + return ret; + } + + debug("DDR: %s triggered successly\n", __func__); + return 0; +} + +static int poll_hmc_clock_status(void) +{ + return wait_for_bit_le32(&sysmgr_regs->hmc_clk, + SYSMGR_HMC_CLK_STATUS_MSK, true, 1000, false); +} + +/** + * sdram_mmr_init_full() - Function to initialize SDRAM MMR + * + * Initialize the SDRAM MMR. + */ +int sdram_mmr_init_full(unsigned int unused) +{ + u32 update_value, io48_value, ddrioctl; + u32 i; + int ret; + + /* Enable access to DDR from CPU master */ + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_DDRREG), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_MEMSPACE0), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_MEMSPACE1A), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_MEMSPACE1B), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_MEMSPACE1C), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_MEMSPACE1D), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_CPU0_MPRT_ADBASE_MEMSPACE1E), + CCU_ADBASE_DI_MASK); + + /* Enable access to DDR from IO master */ + clrbits_le32(CCU_REG_ADDR(CCU_IOM_MPRT_ADBASE_MEMSPACE0), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_IOM_MPRT_ADBASE_MEMSPACE1A), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_IOM_MPRT_ADBASE_MEMSPACE1B), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_IOM_MPRT_ADBASE_MEMSPACE1C), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_IOM_MPRT_ADBASE_MEMSPACE1D), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_IOM_MPRT_ADBASE_MEMSPACE1E), + CCU_ADBASE_DI_MASK); + + /* this enables nonsecure access to DDR */ + /* mpuregion0addr_limit */ + FW_MPU_DDR_SCR_WRITEL(0xFFFF0000, FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMIT); + FW_MPU_DDR_SCR_WRITEL(0x1F, FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT); + + /* nonmpuregion0addr_limit */ + FW_MPU_DDR_SCR_WRITEL(0xFFFF0000, + FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT); + FW_MPU_DDR_SCR_WRITEL(0x1F, FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT); + + /* Enable mpuregion0enable and nonmpuregion0enable */ + FW_MPU_DDR_SCR_WRITEL(MPUREGION0_ENABLE | NONMPUREGION0_ENABLE, + FW_MPU_DDR_SCR_EN_SET); + + /* Ensure HMC clock is running */ + if (poll_hmc_clock_status()) { + puts("DDR: Error as HMC clock not running\n"); + return -1; + } + + /* release DDR scheduler from reset */ + socfpga_per_reset(SOCFPGA_RESET(SDR), 0); + + /* Try 3 times to do a calibration */ + for (i = 0; i < 3; i++) { + ret = wait_for_bit_le32((const void *)(SOCFPGA_SDR_ADDRESS + + DDRCALSTAT), + DDR_HMC_DDRCALSTAT_CAL_MSK, true, 1000, + false); + if (!ret) + break; + + emif_reset(); + } + + if (ret) { + puts("DDR: Error as SDRAM calibration failed\n"); + return -1; + } + debug("DDR: Calibration success\n"); + + u32 ctrlcfg0 = hmc_readl(CTRLCFG0); + u32 ctrlcfg1 = hmc_readl(CTRLCFG1); + u32 dramaddrw = hmc_readl(DRAMADDRW); + u32 dramtim0 = hmc_readl(DRAMTIMING0); + u32 caltim0 = hmc_readl(CALTIMING0); + u32 caltim1 = hmc_readl(CALTIMING1); + u32 caltim2 = hmc_readl(CALTIMING2); + u32 caltim3 = hmc_readl(CALTIMING3); + u32 caltim4 = hmc_readl(CALTIMING4); + u32 caltim9 = hmc_readl(CALTIMING9); + + /* + * Configure the DDR IO size [0xFFCFB008] + * niosreserve0: Used to indicate DDR width & + * bit[7:0] = Number of data bits (bit[6:5] 0x01=32bit, 0x10=64bit) + * bit[8] = 1 if user-mode OCT is present + * bit[9] = 1 if warm reset compiled into EMIF Cal Code + * bit[10] = 1 if warm reset is on during generation in EMIF Cal + * niosreserve1: IP ADCDS version encoded as 16 bit value + * bit[2:0] = Variant (0=not special,1=FAE beta, 2=Customer beta, + * 3=EAP, 4-6 are reserved) + * bit[5:3] = Service Pack # (e.g. 1) + * bit[9:6] = Minor Release # + * bit[14:10] = Major Release # + */ + update_value = hmc_readl(NIOSRESERVED0); + hmc_ecc_writel(((update_value & 0xFF) >> 5), DDRIOCTRL); + ddrioctl = hmc_ecc_readl(DDRIOCTRL); + + /* enable HPS interface to HMC */ + hmc_ecc_writel(DDR_HMC_HPSINTFCSEL_ENABLE_MASK, HPSINTFCSEL); + + /* Set the DDR Configuration */ + io48_value = DDR_CONFIG(CTRLCFG1_CFG_ADDR_ORDER(ctrlcfg1), + (DRAMADDRW_CFG_BANK_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_BANK_GRP_ADDR_WIDTH(dramaddrw)), + DRAMADDRW_CFG_COL_ADDR_WIDTH(dramaddrw), + DRAMADDRW_CFG_ROW_ADDR_WIDTH(dramaddrw)); + + update_value = match_ddr_conf(io48_value); + if (update_value) + ddr_sch_writel(update_value, DDR_SCH_DDRCONF); + + /* Configure HMC dramaddrw */ + hmc_ecc_writel(hmc_readl(DRAMADDRW), DRAMADDRWIDTH); + + /* + * Configure DDR timing + * RDTOMISS = tRTP + tRP + tRCD - BL/2 + * WRTOMISS = WL + tWR + tRP + tRCD and + * WL = RL + BL/2 + 2 - rd-to-wr ; tWR = 15ns so... + * First part of equation is in memory clock units so divide by 2 + * for HMC clock units. 1066MHz is close to 1ns so use 15 directly. + * WRTOMISS = ((RL + BL/2 + 2 + tWR) >> 1)- rd-to-wr + tRP + tRCD + */ + u32 burst_len = CTRLCFG0_CFG_CTRL_BURST_LEN(ctrlcfg0); + + update_value = CALTIMING2_CFG_RD_TO_WR_PCH(caltim2) + + CALTIMING4_CFG_PCH_TO_VALID(caltim4) + + CALTIMING0_CFG_ACT_TO_RDWR(caltim0) - + (burst_len >> 2); + io48_value = (((DRAMTIMING0_CFG_TCL(dramtim0) + 2 + DDR_TWR + + (burst_len >> 1)) >> 1) - + /* Up to here was in memory cycles so divide by 2 */ + CALTIMING1_CFG_RD_TO_WR(caltim1) + + CALTIMING0_CFG_ACT_TO_RDWR(caltim0) + + CALTIMING4_CFG_PCH_TO_VALID(caltim4)); + + ddr_sch_writel(((CALTIMING0_CFG_ACT_TO_ACT(caltim0) << + DDR_SCH_DDRTIMING_ACTTOACT_OFF) | + (update_value << DDR_SCH_DDRTIMING_RDTOMISS_OFF) | + (io48_value << DDR_SCH_DDRTIMING_WRTOMISS_OFF) | + ((burst_len >> 2) << DDR_SCH_DDRTIMING_BURSTLEN_OFF) | + (CALTIMING1_CFG_RD_TO_WR(caltim1) << + DDR_SCH_DDRTIMING_RDTOWR_OFF) | + (CALTIMING3_CFG_WR_TO_RD(caltim3) << + DDR_SCH_DDRTIMING_WRTORD_OFF) | + (((ddrioctl == 1) ? 1 : 0) << + DDR_SCH_DDRTIMING_BWRATIO_OFF)), + DDR_SCH_DDRTIMING); + + /* Configure DDR mode [precharge = 0] */ + ddr_sch_writel(((ddrioctl ? 0 : 1) << + DDR_SCH_DDRMOD_BWRATIOEXTENDED_OFF), + DDR_SCH_DDRMODE); + + /* Configure the read latency */ + ddr_sch_writel((DRAMTIMING0_CFG_TCL(dramtim0) >> 1) + + DDR_READ_LATENCY_DELAY, + DDR_SCH_READ_LATENCY); + + /* + * Configuring timing values concerning activate commands + * [FAWBANK alway 1 because always 4 bank DDR] + */ + ddr_sch_writel(((CALTIMING0_CFG_ACT_TO_ACT_DB(caltim0) << + DDR_SCH_ACTIVATE_RRD_OFF) | + (CALTIMING9_CFG_4_ACT_TO_ACT(caltim9) << + DDR_SCH_ACTIVATE_FAW_OFF) | + (DDR_ACTIVATE_FAWBANK << + DDR_SCH_ACTIVATE_FAWBANK_OFF)), + DDR_SCH_ACTIVATE); + + /* + * Configuring timing values concerning device to device data bus + * ownership change + */ + ddr_sch_writel(((CALTIMING1_CFG_RD_TO_RD_DC(caltim1) << + DDR_SCH_DEVTODEV_BUSRDTORD_OFF) | + (CALTIMING1_CFG_RD_TO_WR_DC(caltim1) << + DDR_SCH_DEVTODEV_BUSRDTOWR_OFF) | + (CALTIMING3_CFG_WR_TO_RD_DC(caltim3) << + DDR_SCH_DEVTODEV_BUSWRTORD_OFF)), + DDR_SCH_DEVTODEV); + + /* assigning the SDRAM size */ + unsigned long long size = sdram_calculate_size(); + /* If the size is invalid, use default Config size */ + if (size <= 0) + gd->ram_size = PHYS_SDRAM_1_SIZE; + else + gd->ram_size = size; + + /* Enable or disable the SDRAM ECC */ + if (CTRLCFG1_CFG_CTRL_EN_ECC(ctrlcfg1)) { + setbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL1, + (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_ECC_EN_SET_MSK)); + clrbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL1, + (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_CNT_RST_SET_MSK)); + setbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL2, + (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | + DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); + } else { + clrbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL1, + (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_ECC_EN_SET_MSK)); + clrbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL2, + (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | + DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); + } + + debug("DDR: HMC init success\n"); + return 0; +} + +/** + * sdram_calculate_size() - Calculate SDRAM size + * + * Calculate SDRAM device size based on SDRAM controller parameters. + * Size is specified in bytes. + */ +unsigned long sdram_calculate_size(void) +{ + u32 dramaddrw = hmc_readl(DRAMADDRW); + + u32 size = 1 << (DRAMADDRW_CFG_CS_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_BANK_GRP_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_BANK_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_ROW_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_COL_ADDR_WIDTH(dramaddrw)); + + size *= (2 << (hmc_ecc_readl(DDRIOCTRL) & + DDR_HMC_DDRIOCTRL_IOSIZE_MSK)); + + return size; +} -- cgit v1.2.3 From 93a8ed868583460ab9f3796fdc92f4713bf759a9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 May 2018 18:04:15 +0200 Subject: ddr: altera: Drop custom dram_bank_mmu_setup() on Arria10 This function was never used in SPL and the default implementation of dram_bank_mmu_setup() does the same thing. The only difference is the part which configures OCRAM as cachable, which doesn't really work as it covers more than the OCRAM. Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen --- drivers/ddr/altera/sdram_arria10.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'drivers/ddr') diff --git a/drivers/ddr/altera/sdram_arria10.c b/drivers/ddr/altera/sdram_arria10.c index 706a038b888..1f2b7f48199 100644 --- a/drivers/ddr/altera/sdram_arria10.c +++ b/drivers/ddr/altera/sdram_arria10.c @@ -713,28 +713,3 @@ int ddr_calibration_sequence(void) return 0; } - -void dram_bank_mmu_setup(int bank) -{ - bd_t *bd = gd->bd; - int i; - - debug("%s: bank: %d\n", __func__, bank); - for (i = bd->bi_dram[bank].start >> 20; - i < (bd->bi_dram[bank].start + bd->bi_dram[bank].size) >> 20; - i++) { -#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) - set_section_dcache(i, DCACHE_WRITETHROUGH); -#else - set_section_dcache(i, DCACHE_WRITEBACK); -#endif - } - - /* same as above but just that we would want cacheable for ocram too */ - i = CONFIG_SYS_INIT_RAM_ADDR >> 20; -#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) - set_section_dcache(i, DCACHE_WRITETHROUGH); -#else - set_section_dcache(i, DCACHE_WRITEBACK); -#endif -} -- cgit v1.2.3 From 07252f6f7e37e23cb43245dcddf8ea8f1d45dec1 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 28 May 2018 17:22:47 +0200 Subject: ddr: altera: Add ECC DRAM scrubbing support for Arria10 The SDRAM must first be rewritten by zeroes if ECC is used to initialize the ECC metadata. Make the CPU overwrite the DRAM with zeroes in such a case. This scrubbing implementation turns the caches on temporarily, then overwrites the whole RAM with zeroes, flushes the caches and turns them off again. This provides satisfactory performance. Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen --- drivers/ddr/altera/sdram_arria10.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/ddr') diff --git a/drivers/ddr/altera/sdram_arria10.c b/drivers/ddr/altera/sdram_arria10.c index 1f2b7f48199..29ea7492f30 100644 --- a/drivers/ddr/altera/sdram_arria10.c +++ b/drivers/ddr/altera/sdram_arria10.c @@ -215,6 +215,30 @@ static int ddr_setup(void) return 0; } +static int sdram_is_ecc_enabled(void) +{ + return !!(readl(&socfpga_ecc_hmc_base->eccctrl) & + ALT_ECC_HMC_OCP_ECCCTL_ECC_EN_SET_MSK); +} + +/* Initialize SDRAM ECC bits to avoid false DBE */ +static void sdram_init_ecc_bits(u32 size) +{ + icache_enable(); + + memset(0, 0, 0x8000); + gd->arch.tlb_addr = 0x4000; + gd->arch.tlb_size = PGTABLE_SIZE; + + dcache_enable(); + + printf("DDRCAL: Scrubbing ECC RAM (%i MiB).\n", size >> 20); + memset((void *)0x8000, 0, size - 0x8000); + flush_dcache_all(); + printf("DDRCAL: Scrubbing ECC RAM done.\n"); + dcache_disable(); +} + /* Function to startup the SDRAM*/ static int sdram_startup(void) { @@ -711,5 +735,8 @@ int ddr_calibration_sequence(void) if (of_sdram_firewall_setup(gd->fdt_blob)) puts("FW: Error Configuring Firewall\n"); + if (sdram_is_ecc_enabled()) + sdram_init_ecc_bits(gd->ram_size); + return 0; } -- cgit v1.2.3