diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/ddr/altera | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/ddr/altera')
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/Kconfig | 8 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/Makefile | 14 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_agilex.c | 171 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_arria10.c | 714 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_gen5.c | 655 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_s10.c | 338 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_s10.h | 42 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_soc64.c | 311 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sdram_soc64.h | 187 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sequencer.c | 3991 | ||||
-rw-r--r-- | roms/u-boot/drivers/ddr/altera/sequencer.h | 284 |
11 files changed, 6715 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/ddr/altera/Kconfig b/roms/u-boot/drivers/ddr/altera/Kconfig new file mode 100644 index 000000000..4660d20de --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/Kconfig @@ -0,0 +1,8 @@ +config SPL_ALTERA_SDRAM + bool "SoCFPGA DDR SDRAM driver in SPL" + depends on SPL + depends on TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_ARRIA10 || TARGET_SOCFPGA_SOC64 + select RAM if TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_SOC64 + select SPL_RAM if TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_SOC64 + help + Enable DDR SDRAM controller for the SoCFPGA devices. diff --git a/roms/u-boot/drivers/ddr/altera/Makefile b/roms/u-boot/drivers/ddr/altera/Makefile new file mode 100644 index 000000000..39dfee5d5 --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2010, Thomas Chou <thomas@wytron.com.tw> +# Copyright (C) 2014 Altera Corporation <www.altera.com> + +ifdef CONFIG_$(SPL_)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_soc64.o sdram_s10.o +obj-$(CONFIG_TARGET_SOCFPGA_AGILEX) += sdram_soc64.o sdram_agilex.o +endif diff --git a/roms/u-boot/drivers/ddr/altera/sdram_agilex.c b/roms/u-boot/drivers/ddr/altera/sdram_agilex.c new file mode 100644 index 000000000..65ecdd022 --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_agilex.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + * + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <div64.h> +#include <fdtdec.h> +#include <hang.h> +#include <log.h> +#include <ram.h> +#include <reset.h> +#include <asm/global_data.h> +#include "sdram_soc64.h" +#include <wait_bit.h> +#include <asm/arch/firewall.h> +#include <asm/arch/reset_manager.h> +#include <asm/arch/system_manager.h> +#include <asm/io.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; + +int sdram_mmr_init_full(struct udevice *dev) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + struct altera_sdram_priv *priv = dev_get_priv(dev); + u32 i; + int ret; + phys_size_t hw_size; + struct bd_info bd = {0}; + + /* Ensure HMC clock is running */ + if (poll_hmc_clock_status()) { + debug("DDR: Error as HMC clock was not running\n"); + return -EPERM; + } + + /* Trying 3 times to do a calibration */ + for (i = 0; i < 3; i++) { + ret = wait_for_bit_le32((const void *)(plat->hmc + + DDRCALSTAT), + DDR_HMC_DDRCALSTAT_CAL_MSK, true, 1000, + false); + if (!ret) + break; + + emif_reset(plat); + } + + if (ret) { + puts("DDR: Error as SDRAM calibration failed\n"); + return -EPERM; + } + debug("DDR: Calibration success\n"); + + /* + * Configure the DDR IO size + * 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 # + */ + /* Configure DDR IO size x16, x32 and x64 mode */ + u32 update_value; + + update_value = hmc_readl(plat, NIOSRESERVED0); + update_value = (update_value & 0xFF) >> 5; + + /* Configure DDR data rate 0-HAlf-rate 1-Quarter-rate */ + update_value |= (hmc_readl(plat, CTRLCFG3) & 0x4); + hmc_ecc_writel(plat, update_value, DDRIOCTRL); + + /* Copy values MMR IOHMC dramaddrw to HMC adp DRAMADDRWIDTH */ + hmc_ecc_writel(plat, hmc_readl(plat, DRAMADDRW), DRAMADDRWIDTH); + + /* assigning the SDRAM size */ + phys_size_t size = sdram_calculate_size(plat); + + if (size <= 0) + hw_size = PHYS_SDRAM_1_SIZE; + else + hw_size = size; + + /* Get bank configuration from devicetree */ + ret = fdtdec_decode_ram_size(gd->fdt_blob, NULL, 0, NULL, + (phys_size_t *)&gd->ram_size, &bd); + if (ret) { + puts("DDR: Failed to decode memory node\n"); + return -ENXIO; + } + + if (gd->ram_size != hw_size) { + printf("DDR: Warning: DRAM size from device tree (%lld MiB)\n", + gd->ram_size >> 20); + printf(" mismatch with hardware (%lld MiB).\n", + hw_size >> 20); + } + + if (gd->ram_size > hw_size) { + printf("DDR: Error: DRAM size from device tree is greater\n"); + printf(" than hardware size.\n"); + hang(); + } + + printf("DDR: %lld MiB\n", gd->ram_size >> 20); + + /* This enables nonsecure access to DDR */ + /* mpuregion0addr_limit */ + FW_MPU_DDR_SCR_WRITEL(gd->ram_size - 1, + 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(gd->ram_size - 1, + FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT); + + /* Enable mpuregion0enable and nonmpuregion0enable */ + FW_MPU_DDR_SCR_WRITEL(MPUREGION0_ENABLE | NONMPUREGION0_ENABLE, + FW_MPU_DDR_SCR_EN_SET); + + u32 ctrlcfg1 = hmc_readl(plat, CTRLCFG1); + + /* Enable or disable the DDR ECC */ + if (CTRLCFG1_CFG_CTRL_EN_ECC(ctrlcfg1)) { + setbits_le32(plat->hmc + 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(plat->hmc + ECCCTRL1, + (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_CNT_RST_SET_MSK)); + setbits_le32(plat->hmc + ECCCTRL2, + (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | + DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); + setbits_le32(plat->hmc + ERRINTEN, + DDR_HMC_ERRINTEN_DERRINTEN_EN_SET_MSK); + + if (!cpu_has_been_warmreset()) + sdram_init_ecc_bits(&bd); + } else { + clrbits_le32(plat->hmc + 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(plat->hmc + ECCCTRL2, + (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | + DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); + } + + /* Enable non-secure reads/writes to HMC Adapter for SDRAM ECC */ + writel(FW_HMC_ADAPTOR_MPU_MASK, FW_HMC_ADAPTOR_REG_ADDR); + + sdram_size_check(&bd); + + priv->info.base = bd.bi_dram[0].start; + priv->info.size = gd->ram_size; + + debug("DDR: HMC init success\n"); + return 0; +} diff --git a/roms/u-boot/drivers/ddr/altera/sdram_arria10.c b/roms/u-boot/drivers/ddr/altera/sdram_arria10.c new file mode 100644 index 000000000..4a8f8dea1 --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_arria10.c @@ -0,0 +1,714 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <errno.h> +#include <fdtdec.h> +#include <init.h> +#include <log.h> +#include <malloc.h> +#include <wait_bit.h> +#include <watchdog.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/fpga_manager.h> +#include <asm/arch/misc.h> +#include <asm/arch/reset_manager.h> +#include <asm/arch/sdram.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/kernel.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void sdram_mmr_init(void); +static u64 sdram_size_calc(void); + +/* FAWBANK - Number of Bank of a given device involved in the FAW period. */ +#define ARRIA10_SDR_ACTIVATE_FAWBANK (0x1) + +#define ARRIA_DDR_CONFIG(A, B, C, R) \ + (((A) << 24) | ((B) << 16) | ((C) << 8) | (R)) +#define DDR_CONFIG_ELEMENTS ARRAY_SIZE(ddr_config) +#define DDR_REG_SEQ2CORE 0xFFD0507C +#define DDR_REG_CORE2SEQ 0xFFD05078 +#define DDR_READ_LATENCY_DELAY 40 +#define DDR_SIZE_2GB_HEX 0x80000000 + +#define IO48_MMR_DRAMSTS 0xFFCFA0EC +#define IO48_MMR_NIOS2_RESERVE0 0xFFCFA110 +#define IO48_MMR_NIOS2_RESERVE1 0xFFCFA114 +#define IO48_MMR_NIOS2_RESERVE2 0xFFCFA118 + +#define SEQ2CORE_MASK 0xF +#define CORE2SEQ_INT_REQ 0xF +#define SEQ2CORE_INT_RESP_BIT 3 + +static const struct socfpga_ecc_hmc *socfpga_ecc_hmc_base = + (void *)SOCFPGA_SDR_ADDRESS; +static const struct socfpga_noc_ddr_scheduler *socfpga_noc_ddr_scheduler_base = + (void *)SOCFPGA_SDR_SCHEDULER_ADDRESS; +static const struct socfpga_noc_fw_ddr_mpu_fpga2sdram + *socfpga_noc_fw_ddr_mpu_fpga2sdram_base = + (void *)SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS; +static const struct socfpga_noc_fw_ddr_l3 *socfpga_noc_fw_ddr_l3_base = + (void *)SOCFPGA_SDR_FIREWALL_L3_ADDRESS; +static const struct socfpga_io48_mmr *socfpga_io48_mmr_base = + (void *)SOCFPGA_HMC_MMR_IO48_ADDRESS; + +/* The following are the supported configurations */ +static u32 ddr_config[] = { + /* Chip - Row - Bank - Column Style */ + /* All Types */ + ARRIA_DDR_CONFIG(0, 3, 10, 12), + ARRIA_DDR_CONFIG(0, 3, 10, 13), + ARRIA_DDR_CONFIG(0, 3, 10, 14), + ARRIA_DDR_CONFIG(0, 3, 10, 15), + ARRIA_DDR_CONFIG(0, 3, 10, 16), + ARRIA_DDR_CONFIG(0, 3, 10, 17), + /* LPDDR x16 */ + ARRIA_DDR_CONFIG(0, 3, 11, 14), + ARRIA_DDR_CONFIG(0, 3, 11, 15), + ARRIA_DDR_CONFIG(0, 3, 11, 16), + ARRIA_DDR_CONFIG(0, 3, 12, 15), + /* DDR4 Only */ + ARRIA_DDR_CONFIG(0, 4, 10, 14), + ARRIA_DDR_CONFIG(0, 4, 10, 15), + ARRIA_DDR_CONFIG(0, 4, 10, 16), + ARRIA_DDR_CONFIG(0, 4, 10, 17), /* 14 */ + /* Chip - Bank - Row - Column Style */ + ARRIA_DDR_CONFIG(1, 3, 10, 12), + ARRIA_DDR_CONFIG(1, 3, 10, 13), + ARRIA_DDR_CONFIG(1, 3, 10, 14), + ARRIA_DDR_CONFIG(1, 3, 10, 15), + ARRIA_DDR_CONFIG(1, 3, 10, 16), + ARRIA_DDR_CONFIG(1, 3, 10, 17), + ARRIA_DDR_CONFIG(1, 3, 11, 14), + ARRIA_DDR_CONFIG(1, 3, 11, 15), + ARRIA_DDR_CONFIG(1, 3, 11, 16), + ARRIA_DDR_CONFIG(1, 3, 12, 15), + /* DDR4 Only */ + ARRIA_DDR_CONFIG(1, 4, 10, 14), + ARRIA_DDR_CONFIG(1, 4, 10, 15), + ARRIA_DDR_CONFIG(1, 4, 10, 16), + ARRIA_DDR_CONFIG(1, 4, 10, 17), +}; + +static int match_ddr_conf(u32 ddr_conf) +{ + int i; + + for (i = 0; i < DDR_CONFIG_ELEMENTS; i++) { + if (ddr_conf == ddr_config[i]) + return i; + } + return 0; +} + +static int emif_clear(void) +{ + writel(0, DDR_REG_CORE2SEQ); + + return wait_for_bit_le32((u32 *)DDR_REG_SEQ2CORE, + SEQ2CORE_MASK, 0, 1000, 0); +} + +static int emif_reset(void) +{ + u32 c2s, s2c; + int ret; + + c2s = readl(DDR_REG_CORE2SEQ); + s2c = readl(DDR_REG_SEQ2CORE); + + debug("c2s=%08x s2c=%08x nr0=%08x nr1=%08x nr2=%08x dst=%08x\n", + c2s, s2c, readl(IO48_MMR_NIOS2_RESERVE0), + readl(IO48_MMR_NIOS2_RESERVE1), + readl(IO48_MMR_NIOS2_RESERVE2), + readl(IO48_MMR_DRAMSTS)); + + if (s2c & SEQ2CORE_MASK) { + ret = emif_clear(); + if (ret) { + debug("failed emif_clear()\n"); + return -EPERM; + } + } + + writel(CORE2SEQ_INT_REQ, DDR_REG_CORE2SEQ); + + ret = wait_for_bit_le32((u32 *)DDR_REG_SEQ2CORE, + SEQ2CORE_INT_RESP_BIT, false, 1000, false); + if (ret) { + debug("emif_reset failed to see interrupt acknowledge\n"); + emif_clear(); + return ret; + } + + mdelay(1); + + ret = emif_clear(); + if (ret) { + debug("emif_clear() failed\n"); + return -EPERM; + } + debug("emif_reset interrupt cleared\n"); + + debug("nr0=%08x nr1=%08x nr2=%08x\n", + readl(IO48_MMR_NIOS2_RESERVE0), + readl(IO48_MMR_NIOS2_RESERVE1), + readl(IO48_MMR_NIOS2_RESERVE2)); + + return 0; +} + +static int ddr_setup(void) +{ + int i, ret; + + /* Try 32 times to do a calibration */ + for (i = 0; i < 32; i++) { + mdelay(500); + ret = wait_for_bit_le32(&socfpga_ecc_hmc_base->ddrcalstat, + BIT(0), true, 500, false); + if (!ret) + return 0; + + ret = emif_reset(); + if (ret) + puts("Error: Failed to reset EMIF\n"); + } + + puts("Error: Could Not Calibrate SDRAM\n"); + return -EPERM; +} + +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) +{ + /* Release NOC ddr scheduler from reset */ + socfpga_reset_deassert_noc_ddr_scheduler(); + + /* Bringup the DDR (calibration and configuration) */ + return ddr_setup(); +} + +static u64 sdram_size_calc(void) +{ + u32 dramaddrw = readl(&socfpga_io48_mmr_base->dramaddrw); + + u64 size = BIT(((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_CS_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_CS_ADDR_WIDTH_SHIFT) + + ((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_BANK_GROUP_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_BANK_GROUP_ADDR_WIDTH_SHIFT) + + ((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_BANK_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_BANK_ADDR_WIDTH_SHIFT) + + ((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_ROW_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_ROW_ADDR_WIDTH_SHIFT) + + (dramaddrw & IO48_MMR_DRAMADDRW_CFG_COL_ADDR_WIDTH_MASK)); + + size *= (2 << (readl(&socfpga_ecc_hmc_base->ddrioctrl) & + ALT_ECC_HMC_OCP_DDRIOCTRL_IO_SIZE_MSK)); + + debug("SDRAM size=%llu\n", size); + + return size; +} + +/* Function to initialize SDRAM MMR and NOC DDR scheduler*/ +static void sdram_mmr_init(void) +{ + u32 update_value, io48_value; + u32 ctrlcfg0 = readl(&socfpga_io48_mmr_base->ctrlcfg0); + u32 ctrlcfg1 = readl(&socfpga_io48_mmr_base->ctrlcfg1); + u32 dramaddrw = readl(&socfpga_io48_mmr_base->dramaddrw); + u32 caltim0 = readl(&socfpga_io48_mmr_base->caltiming0); + u32 caltim1 = readl(&socfpga_io48_mmr_base->caltiming1); + u32 caltim2 = readl(&socfpga_io48_mmr_base->caltiming2); + u32 caltim3 = readl(&socfpga_io48_mmr_base->caltiming3); + u32 caltim4 = readl(&socfpga_io48_mmr_base->caltiming4); + u32 caltim9 = readl(&socfpga_io48_mmr_base->caltiming9); + u32 ddrioctl; + + /* + * Configure the DDR IO size [0xFFCFB008] + * niosreserve0: Used to indicate DDR width & + * bit[7:0] = Number of data bits (0x20 for 32bit) + * 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 # + */ + if ((readl(&socfpga_io48_mmr_base->niosreserve1) >> 6) & 0x1FF) { + update_value = readl(&socfpga_io48_mmr_base->niosreserve0); + writel(((update_value & 0xFF) >> 5), + &socfpga_ecc_hmc_base->ddrioctrl); + } + + ddrioctl = readl(&socfpga_ecc_hmc_base->ddrioctrl); + + /* Set the DDR Configuration [0xFFD12400] */ + io48_value = ARRIA_DDR_CONFIG( + ((ctrlcfg1 & + IO48_MMR_CTRLCFG1_ADDR_ORDER_MASK) >> + IO48_MMR_CTRLCFG1_ADDR_ORDER_SHIFT), + ((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_BANK_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_BANK_ADDR_WIDTH_SHIFT) + + ((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_BANK_GROUP_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_BANK_GROUP_ADDR_WIDTH_SHIFT), + (dramaddrw & + IO48_MMR_DRAMADDRW_CFG_COL_ADDR_WIDTH_MASK), + ((dramaddrw & + IO48_MMR_DRAMADDRW_CFG_ROW_ADDR_WIDTH_MASK) >> + IO48_MMR_DRAMADDRW_CFG_ROW_ADDR_WIDTH_SHIFT)); + + update_value = match_ddr_conf(io48_value); + if (update_value) + writel(update_value, + &socfpga_noc_ddr_scheduler_base->ddr_t_main_scheduler_ddrconf); + + /* + * Configure DDR timing [0xFFD1240C] + * 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 ctrlcfg0_cfg_ctrl_burst_len = + (ctrlcfg0 & IO48_MMR_CTRLCFG0_CTRL_BURST_LENGTH_MASK) >> + IO48_MMR_CTRLCFG0_CTRL_BURST_LENGTH_SHIFT; + + u32 caltim0_cfg_act_to_rdwr = caltim0 & + IO48_MMR_CALTIMING0_CFG_ACT_TO_RDWR_MASK; + + u32 caltim0_cfg_act_to_act = + (caltim0 & IO48_MMR_CALTIMING0_CFG_ACT_TO_ACT_MASK) >> + IO48_MMR_CALTIMING0_CFG_ACT_TO_ACT_SHIFT; + + u32 caltim0_cfg_act_to_act_db = + (caltim0 & + IO48_MMR_CALTIMING0_CFG_ACT_TO_ACT_DIFF_BANK_MASK) >> + IO48_MMR_CALTIMING0_CFG_ACT_TO_ACT_DIFF_BANK_SHIFT; + + u32 caltim1_cfg_rd_to_wr = + (caltim1 & IO48_MMR_CALTIMING1_CFG_RD_TO_WR_MASK) >> + IO48_MMR_CALTIMING1_CFG_RD_TO_WR_SHIFT; + + u32 caltim1_cfg_rd_to_rd_dc = + (caltim1 & IO48_MMR_CALTIMING1_CFG_RD_TO_RD_DC_MASK) >> + IO48_MMR_CALTIMING1_CFG_RD_TO_RD_DC_SHIFT; + + u32 caltim1_cfg_rd_to_wr_dc = + (caltim1 & IO48_MMR_CALTIMING1_CFG_RD_TO_WR_DIFF_CHIP_MASK) >> + IO48_MMR_CALTIMING1_CFG_RD_TO_WR_DIFF_CHIP_SHIFT; + + u32 caltim2_cfg_rd_to_pch = + (caltim2 & IO48_MMR_CALTIMING2_CFG_RD_TO_PCH_MASK) >> + IO48_MMR_CALTIMING2_CFG_RD_TO_PCH_SHIFT; + + u32 caltim3_cfg_wr_to_rd = + (caltim3 & IO48_MMR_CALTIMING3_CFG_WR_TO_RD_MASK) >> + IO48_MMR_CALTIMING3_CFG_WR_TO_RD_SHIFT; + + u32 caltim3_cfg_wr_to_rd_dc = + (caltim3 & IO48_MMR_CALTIMING3_CFG_WR_TO_RD_DIFF_CHIP_MASK) >> + IO48_MMR_CALTIMING3_CFG_WR_TO_RD_DIFF_CHIP_SHIFT; + + u32 caltim4_cfg_pch_to_valid = + (caltim4 & IO48_MMR_CALTIMING4_CFG_PCH_TO_VALID_MASK) >> + IO48_MMR_CALTIMING4_CFG_PCH_TO_VALID_SHIFT; + + u32 caltim9_cfg_4_act_to_act = caltim9 & + IO48_MMR_CALTIMING9_CFG_WR_4_ACT_TO_ACT_MASK; + + update_value = (caltim2_cfg_rd_to_pch + caltim4_cfg_pch_to_valid + + caltim0_cfg_act_to_rdwr - + (ctrlcfg0_cfg_ctrl_burst_len >> 2)); + + io48_value = ((((readl(&socfpga_io48_mmr_base->dramtiming0) & + ALT_IO48_DRAMTIME_MEM_READ_LATENCY_MASK) + 2 + 15 + + (ctrlcfg0_cfg_ctrl_burst_len >> 1)) >> 1) - + /* Up to here was in memory cycles so divide by 2 */ + caltim1_cfg_rd_to_wr + caltim0_cfg_act_to_rdwr + + caltim4_cfg_pch_to_valid); + + writel(((caltim0_cfg_act_to_act << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_ACTTOACT_LSB) | + (update_value << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_RDTOMISS_LSB) | + (io48_value << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_WRTOMISS_LSB) | + ((ctrlcfg0_cfg_ctrl_burst_len >> 2) << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_BURSTLEN_LSB) | + (caltim1_cfg_rd_to_wr << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_RDTOWR_LSB) | + (caltim3_cfg_wr_to_rd << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_WRTORD_LSB) | + (((ddrioctl == 1) ? 1 : 0) << + ALT_NOC_MPU_DDR_T_SCHED_DDRTIMING_BWRATIO_LSB)), + &socfpga_noc_ddr_scheduler_base-> + ddr_t_main_scheduler_ddrtiming); + + /* Configure DDR mode [0xFFD12410] [precharge = 0] */ + writel(((ddrioctl ? 0 : 1) << + ALT_NOC_MPU_DDR_T_SCHED_DDRMOD_BWRATIOEXTENDED_LSB), + &socfpga_noc_ddr_scheduler_base->ddr_t_main_scheduler_ddrmode); + + /* Configure the read latency [0xFFD12414] */ + writel(((readl(&socfpga_io48_mmr_base->dramtiming0) & + ALT_IO48_DRAMTIME_MEM_READ_LATENCY_MASK) >> 1) + + DDR_READ_LATENCY_DELAY, + &socfpga_noc_ddr_scheduler_base-> + ddr_t_main_scheduler_readlatency); + + /* + * Configuring timing values concerning activate commands + * [0xFFD12438] [FAWBANK alway 1 because always 4 bank DDR] + */ + writel(((caltim0_cfg_act_to_act_db << + ALT_NOC_MPU_DDR_T_SCHED_ACTIVATE_RRD_LSB) | + (caltim9_cfg_4_act_to_act << + ALT_NOC_MPU_DDR_T_SCHED_ACTIVATE_FAW_LSB) | + (ARRIA10_SDR_ACTIVATE_FAWBANK << + ALT_NOC_MPU_DDR_T_SCHED_ACTIVATE_FAWBANK_LSB)), + &socfpga_noc_ddr_scheduler_base->ddr_t_main_scheduler_activate); + + /* + * Configuring timing values concerning device to device data bus + * ownership change [0xFFD1243C] + */ + writel(((caltim1_cfg_rd_to_rd_dc << + ALT_NOC_MPU_DDR_T_SCHED_DEVTODEV_BUSRDTORD_LSB) | + (caltim1_cfg_rd_to_wr_dc << + ALT_NOC_MPU_DDR_T_SCHED_DEVTODEV_BUSRDTOWR_LSB) | + (caltim3_cfg_wr_to_rd_dc << + ALT_NOC_MPU_DDR_T_SCHED_DEVTODEV_BUSWRTORD_LSB)), + &socfpga_noc_ddr_scheduler_base->ddr_t_main_scheduler_devtodev); + + /* Enable or disable the SDRAM ECC */ + if (ctrlcfg1 & IO48_MMR_CTRLCFG1_CTRL_ENABLE_ECC) { + setbits_le32(&socfpga_ecc_hmc_base->eccctrl, + (ALT_ECC_HMC_OCP_ECCCTL_AWB_CNT_RST_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL_CNT_RST_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL_ECC_EN_SET_MSK)); + clrbits_le32(&socfpga_ecc_hmc_base->eccctrl, + (ALT_ECC_HMC_OCP_ECCCTL_AWB_CNT_RST_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL_CNT_RST_SET_MSK)); + setbits_le32(&socfpga_ecc_hmc_base->eccctrl2, + (ALT_ECC_HMC_OCP_ECCCTL2_RMW_EN_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL2_AWB_EN_SET_MSK)); + } else { + clrbits_le32(&socfpga_ecc_hmc_base->eccctrl, + (ALT_ECC_HMC_OCP_ECCCTL_AWB_CNT_RST_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL_CNT_RST_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL_ECC_EN_SET_MSK)); + clrbits_le32(&socfpga_ecc_hmc_base->eccctrl2, + (ALT_ECC_HMC_OCP_ECCCTL2_RMW_EN_SET_MSK | + ALT_ECC_HMC_OCP_ECCCTL2_AWB_EN_SET_MSK)); + } +} + +struct firewall_entry { + const char *prop_name; + const u32 cfg_addr; + const u32 en_addr; + const u32 en_bit; +}; +#define FW_MPU_FPGA_ADDRESS \ + ((const struct socfpga_noc_fw_ddr_mpu_fpga2sdram *)\ + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS) + +#define SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(ADDR) \ + (SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS + \ + offsetof(struct socfpga_noc_fw_ddr_mpu_fpga2sdram, ADDR)) + +#define SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(ADDR) \ + (SOCFPGA_SDR_FIREWALL_L3_ADDRESS + \ + offsetof(struct socfpga_noc_fw_ddr_l3, ADDR)) + +const struct firewall_entry firewall_table[] = { + { + "mpu0", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(mpuregion0addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_MPUREG0EN_SET_MSK + }, + { + "mpu1", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS + + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(mpuregion1addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_MPUREG1EN_SET_MSK + }, + { + "mpu2", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(mpuregion2addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_MPUREG2EN_SET_MSK + }, + { + "mpu3", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(mpuregion3addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_MPUREG3EN_SET_MSK + }, + { + "l3-0", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion0addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG0EN_SET_MSK + }, + { + "l3-1", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion1addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG1EN_SET_MSK + }, + { + "l3-2", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion2addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG2EN_SET_MSK + }, + { + "l3-3", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion3addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG3EN_SET_MSK + }, + { + "l3-4", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion4addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG4EN_SET_MSK + }, + { + "l3-5", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion5addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG5EN_SET_MSK + }, + { + "l3-6", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion6addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG6EN_SET_MSK + }, + { + "l3-7", + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(hpsregion7addr), + SOCFPGA_SDR_FIREWALL_L3_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_HPSREG7EN_SET_MSK + }, + { + "fpga2sdram0-0", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram0region0addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR0REG0EN_SET_MSK + }, + { + "fpga2sdram0-1", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram0region1addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR0REG1EN_SET_MSK + }, + { + "fpga2sdram0-2", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram0region2addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR0REG2EN_SET_MSK + }, + { + "fpga2sdram0-3", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram0region3addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR0REG3EN_SET_MSK + }, + { + "fpga2sdram1-0", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram1region0addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR1REG0EN_SET_MSK + }, + { + "fpga2sdram1-1", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram1region1addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR1REG1EN_SET_MSK + }, + { + "fpga2sdram1-2", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram1region2addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR1REG2EN_SET_MSK + }, + { + "fpga2sdram1-3", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram1region3addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR1REG3EN_SET_MSK + }, + { + "fpga2sdram2-0", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram2region0addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR2REG0EN_SET_MSK + }, + { + "fpga2sdram2-1", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram2region1addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR2REG1EN_SET_MSK + }, + { + "fpga2sdram2-2", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram2region2addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR2REG2EN_SET_MSK + }, + { + "fpga2sdram2-3", + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET + (fpga2sdram2region3addr), + SOCFPGA_SDR_FIREWALL_MPU_FPGA_ADDRESS_OFFSET(enable), + ALT_NOC_FW_DDR_SCR_EN_F2SDR2REG3EN_SET_MSK + }, + +}; + +static int of_sdram_firewall_setup(const void *blob) +{ + int child, i, node, ret; + u32 start_end[2]; + char name[32]; + + node = fdtdec_next_compatible(blob, 0, COMPAT_ALTERA_SOCFPGA_NOC); + if (node < 0) + return -ENXIO; + + child = fdt_first_subnode(blob, node); + if (child < 0) + return -ENXIO; + + /* set to default state */ + writel(0, &socfpga_noc_fw_ddr_mpu_fpga2sdram_base->enable); + writel(0, &socfpga_noc_fw_ddr_l3_base->enable); + + + for (i = 0; i < ARRAY_SIZE(firewall_table); i++) { + sprintf(name, "%s", firewall_table[i].prop_name); + ret = fdtdec_get_int_array(blob, child, name, + start_end, 2); + if (ret) { + sprintf(name, "altr,%s", firewall_table[i].prop_name); + ret = fdtdec_get_int_array(blob, child, name, + start_end, 2); + if (ret) + continue; + } + + writel((start_end[0] & ALT_NOC_FW_DDR_ADDR_MASK) | + (start_end[1] << ALT_NOC_FW_DDR_END_ADDR_LSB), + firewall_table[i].cfg_addr); + setbits_le32(firewall_table[i].en_addr, + firewall_table[i].en_bit); + } + + return 0; +} + +int ddr_calibration_sequence(void) +{ + WATCHDOG_RESET(); + + /* Check to see if SDRAM cal was success */ + if (sdram_startup()) { + puts("DDRCAL: Failed\n"); + return -EPERM; + } + + puts("DDRCAL: Success\n"); + + WATCHDOG_RESET(); + + /* initialize the MMR register */ + sdram_mmr_init(); + + /* assigning the SDRAM size */ + u64 size = sdram_size_calc(); + + /* + * If size is less than zero, this is invalid/weird value from + * calculation, use default Config size. + * Up to 2GB is supported, 2GB would be used if more than that. + */ + if (size <= 0) + gd->ram_size = PHYS_SDRAM_1_SIZE; + else if (DDR_SIZE_2GB_HEX <= size) + gd->ram_size = DDR_SIZE_2GB_HEX; + else + gd->ram_size = (u32)size; + + /* setup the dram info within bd */ + dram_init_banksize(); + + 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; +} diff --git a/roms/u-boot/drivers/ddr/altera/sdram_gen5.c b/roms/u-boot/drivers/ddr/altera/sdram_gen5.c new file mode 100644 index 000000000..8d3ce495d --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_gen5.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright Altera Corporation (C) 2014-2015 + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <div64.h> +#include <init.h> +#include <log.h> +#include <ram.h> +#include <reset.h> +#include <watchdog.h> +#include <asm/arch/fpga_manager.h> +#include <asm/arch/reset_manager.h> +#include <asm/arch/sdram.h> +#include <asm/arch/system_manager.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <dm/device_compat.h> + +#include "sequencer.h" + +#ifdef CONFIG_SPL_BUILD + +struct altera_gen5_sdram_priv { + struct ram_info info; +}; + +struct altera_gen5_sdram_plat { + struct socfpga_sdr *sdr; +}; + +struct sdram_prot_rule { + u32 sdram_start; /* SDRAM start address */ + u32 sdram_end; /* SDRAM end address */ + u32 rule; /* SDRAM protection rule number: 0-19 */ + int valid; /* Rule valid or not? 1 - valid, 0 not*/ + + u32 security; + u32 portmask; + u32 result; + u32 lo_prot_id; + u32 hi_prot_id; +}; + +static unsigned long sdram_calculate_size(struct socfpga_sdr_ctrl *sdr_ctrl); + +/** + * get_errata_rows() - Up the number of DRAM rows to cover entire address space + * @cfg: SDRAM controller configuration data + * + * SDRAM Failure happens when accessing non-existent memory. Artificially + * increase the number of rows so that the memory controller thinks it has + * 4GB of RAM. This function returns such amount of rows. + */ +static int get_errata_rows(const struct socfpga_sdram_config *cfg) +{ + /* Define constant for 4G memory - used for SDRAM errata workaround */ +#define MEMSIZE_4G (4ULL * 1024ULL * 1024ULL * 1024ULL) + const unsigned long long memsize = MEMSIZE_4G; + const unsigned int cs = + ((cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_CSBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_CSBITS_LSB) + 1; + const unsigned int rows = + (cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB; + const unsigned int banks = + (cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_BANKBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_BANKBITS_LSB; + const unsigned int cols = + (cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_COLBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_COLBITS_LSB; + const unsigned int width = 8; + + unsigned long long newrows; + int bits, inewrowslog2; + + debug("workaround rows - memsize %lld\n", memsize); + debug("workaround rows - cs %d\n", cs); + debug("workaround rows - width %d\n", width); + debug("workaround rows - rows %d\n", rows); + debug("workaround rows - banks %d\n", banks); + debug("workaround rows - cols %d\n", cols); + + newrows = lldiv(memsize, cs * (width / 8)); + debug("rows workaround - term1 %lld\n", newrows); + + newrows = lldiv(newrows, (1 << banks) * (1 << cols)); + debug("rows workaround - term2 %lld\n", newrows); + + /* + * Compute the hamming weight - same as number of bits set. + * Need to see if result is ordinal power of 2 before + * attempting log2 of result. + */ + bits = generic_hweight32(newrows); + + debug("rows workaround - bits %d\n", bits); + + if (bits != 1) { + printf("SDRAM workaround failed, bits set %d\n", bits); + return rows; + } + + if (newrows > UINT_MAX) { + printf("SDRAM workaround rangecheck failed, %lld\n", newrows); + return rows; + } + + inewrowslog2 = __ilog2(newrows); + + debug("rows workaround - ilog2 %d, %lld\n", inewrowslog2, newrows); + + if (inewrowslog2 == -1) { + printf("SDRAM workaround failed, newrows %lld\n", newrows); + return rows; + } + + return inewrowslog2; +} + +/* SDRAM protection rules vary from 0-19, a total of 20 rules. */ +static void sdram_set_rule(struct socfpga_sdr_ctrl *sdr_ctrl, + struct sdram_prot_rule *prule) +{ + u32 lo_addr_bits; + u32 hi_addr_bits; + int ruleno = prule->rule; + + /* Select the rule */ + writel(ruleno, &sdr_ctrl->prot_rule_rdwr); + + /* Obtain the address bits */ + lo_addr_bits = prule->sdram_start >> 20ULL; + hi_addr_bits = (prule->sdram_end - 1) >> 20ULL; + + debug("sdram set rule start %x, %d\n", lo_addr_bits, + prule->sdram_start); + debug("sdram set rule end %x, %d\n", hi_addr_bits, + prule->sdram_end); + + /* Set rule addresses */ + writel(lo_addr_bits | (hi_addr_bits << 12), &sdr_ctrl->prot_rule_addr); + + /* Set rule protection ids */ + writel(prule->lo_prot_id | (prule->hi_prot_id << 12), + &sdr_ctrl->prot_rule_id); + + /* Set the rule data */ + writel(prule->security | (prule->valid << 2) | + (prule->portmask << 3) | (prule->result << 13), + &sdr_ctrl->prot_rule_data); + + /* write the rule */ + writel(ruleno | (1 << 5), &sdr_ctrl->prot_rule_rdwr); + + /* Set rule number to 0 by default */ + writel(0, &sdr_ctrl->prot_rule_rdwr); +} + +static void sdram_get_rule(struct socfpga_sdr_ctrl *sdr_ctrl, + struct sdram_prot_rule *prule) +{ + u32 addr; + u32 id; + u32 data; + int ruleno = prule->rule; + + /* Read the rule */ + writel(ruleno, &sdr_ctrl->prot_rule_rdwr); + writel(ruleno | (1 << 6), &sdr_ctrl->prot_rule_rdwr); + + /* Get the addresses */ + addr = readl(&sdr_ctrl->prot_rule_addr); + prule->sdram_start = (addr & 0xFFF) << 20; + prule->sdram_end = ((addr >> 12) & 0xFFF) << 20; + + /* Get the configured protection IDs */ + id = readl(&sdr_ctrl->prot_rule_id); + prule->lo_prot_id = id & 0xFFF; + prule->hi_prot_id = (id >> 12) & 0xFFF; + + /* Get protection data */ + data = readl(&sdr_ctrl->prot_rule_data); + + prule->security = data & 0x3; + prule->valid = (data >> 2) & 0x1; + prule->portmask = (data >> 3) & 0x3FF; + prule->result = (data >> 13) & 0x1; +} + +static void +sdram_set_protection_config(struct socfpga_sdr_ctrl *sdr_ctrl, + const u32 sdram_start, const u32 sdram_end) +{ + struct sdram_prot_rule rule; + int rules; + + /* Start with accepting all SDRAM transaction */ + writel(0x0, &sdr_ctrl->protport_default); + + /* Clear all protection rules for warm boot case */ + memset(&rule, 0, sizeof(rule)); + + for (rules = 0; rules < 20; rules++) { + rule.rule = rules; + sdram_set_rule(sdr_ctrl, &rule); + } + + /* new rule: accept SDRAM */ + rule.sdram_start = sdram_start; + rule.sdram_end = sdram_end; + rule.lo_prot_id = 0x0; + rule.hi_prot_id = 0xFFF; + rule.portmask = 0x3FF; + rule.security = 0x3; + rule.result = 0; + rule.valid = 1; + rule.rule = 0; + + /* set new rule */ + sdram_set_rule(sdr_ctrl, &rule); + + /* default rule: reject everything */ + writel(0x3ff, &sdr_ctrl->protport_default); +} + +static void sdram_dump_protection_config(struct socfpga_sdr_ctrl *sdr_ctrl) +{ + struct sdram_prot_rule rule; + int rules; + + debug("SDRAM Prot rule, default %x\n", + readl(&sdr_ctrl->protport_default)); + + for (rules = 0; rules < 20; rules++) { + rule.rule = rules; + sdram_get_rule(sdr_ctrl, &rule); + debug("Rule %d, rules ...\n", rules); + debug(" sdram start %x\n", rule.sdram_start); + debug(" sdram end %x\n", rule.sdram_end); + debug(" low prot id %d, hi prot id %d\n", + rule.lo_prot_id, + rule.hi_prot_id); + debug(" portmask %x\n", rule.portmask); + debug(" security %d\n", rule.security); + debug(" result %d\n", rule.result); + debug(" valid %d\n", rule.valid); + } +} + +/** + * sdram_write_verify() - write to register and verify the write. + * @addr: Register address + * @val: Value to be written and verified + * + * This function writes to a register, reads back the value and compares + * the result with the written value to check if the data match. + */ +static unsigned sdram_write_verify(const u32 *addr, const u32 val) +{ + u32 rval; + + debug(" Write - Address 0x%p Data 0x%08x\n", addr, val); + writel(val, addr); + + debug(" Read and verify..."); + rval = readl(addr); + if (rval != val) { + debug("FAIL - Address 0x%p Expected 0x%08x Data 0x%08x\n", + addr, val, rval); + return -EINVAL; + } + + debug("correct!\n"); + return 0; +} + +/** + * sdr_get_ctrlcfg() - Get the value of DRAM CTRLCFG register + * @cfg: SDRAM controller configuration data + * + * Return the value of DRAM CTRLCFG register. + */ +static u32 sdr_get_ctrlcfg(const struct socfpga_sdram_config *cfg) +{ + const u32 csbits = + ((cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_CSBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_CSBITS_LSB) + 1; + u32 addrorder = + (cfg->ctrl_cfg & SDR_CTRLGRP_CTRLCFG_ADDRORDER_MASK) >> + SDR_CTRLGRP_CTRLCFG_ADDRORDER_LSB; + + u32 ctrl_cfg = cfg->ctrl_cfg; + + /* + * SDRAM Failure When Accessing Non-Existent Memory + * Set the addrorder field of the SDRAM control register + * based on the CSBITs setting. + */ + if (csbits == 1) { + if (addrorder != 0) + debug("INFO: Changing address order to 0 (chip, row, bank, column)\n"); + addrorder = 0; + } else if (csbits == 2) { + if (addrorder != 2) + debug("INFO: Changing address order to 2 (row, chip, bank, column)\n"); + addrorder = 2; + } + + ctrl_cfg &= ~SDR_CTRLGRP_CTRLCFG_ADDRORDER_MASK; + ctrl_cfg |= addrorder << SDR_CTRLGRP_CTRLCFG_ADDRORDER_LSB; + + return ctrl_cfg; +} + +/** + * sdr_get_addr_rw() - Get the value of DRAM ADDRW register + * @cfg: SDRAM controller configuration data + * + * Return the value of DRAM ADDRW register. + */ +static u32 sdr_get_addr_rw(const struct socfpga_sdram_config *cfg) +{ + /* + * SDRAM Failure When Accessing Non-Existent Memory + * Set SDR_CTRLGRP_DRAMADDRW_CSBITS_LSB to + * log2(number of chip select bits). Since there's only + * 1 or 2 chip selects, log2(1) => 0, and log2(2) => 1, + * which is the same as "chip selects" - 1. + */ + const int rows = get_errata_rows(cfg); + u32 dram_addrw = cfg->dram_addrw & ~SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK; + + return dram_addrw | (rows << SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB); +} + +/** + * sdr_load_regs() - Load SDRAM controller registers + * @cfg: SDRAM controller configuration data + * + * This function loads the register values into the SDRAM controller block. + */ +static void sdr_load_regs(struct socfpga_sdr_ctrl *sdr_ctrl, + const struct socfpga_sdram_config *cfg) +{ + const u32 ctrl_cfg = sdr_get_ctrlcfg(cfg); + const u32 dram_addrw = sdr_get_addr_rw(cfg); + + debug("\nConfiguring CTRLCFG\n"); + writel(ctrl_cfg, &sdr_ctrl->ctrl_cfg); + + debug("Configuring DRAMTIMING1\n"); + writel(cfg->dram_timing1, &sdr_ctrl->dram_timing1); + + debug("Configuring DRAMTIMING2\n"); + writel(cfg->dram_timing2, &sdr_ctrl->dram_timing2); + + debug("Configuring DRAMTIMING3\n"); + writel(cfg->dram_timing3, &sdr_ctrl->dram_timing3); + + debug("Configuring DRAMTIMING4\n"); + writel(cfg->dram_timing4, &sdr_ctrl->dram_timing4); + + debug("Configuring LOWPWRTIMING\n"); + writel(cfg->lowpwr_timing, &sdr_ctrl->lowpwr_timing); + + debug("Configuring DRAMADDRW\n"); + writel(dram_addrw, &sdr_ctrl->dram_addrw); + + debug("Configuring DRAMIFWIDTH\n"); + writel(cfg->dram_if_width, &sdr_ctrl->dram_if_width); + + debug("Configuring DRAMDEVWIDTH\n"); + writel(cfg->dram_dev_width, &sdr_ctrl->dram_dev_width); + + debug("Configuring LOWPWREQ\n"); + writel(cfg->lowpwr_eq, &sdr_ctrl->lowpwr_eq); + + debug("Configuring DRAMINTR\n"); + writel(cfg->dram_intr, &sdr_ctrl->dram_intr); + + debug("Configuring STATICCFG\n"); + writel(cfg->static_cfg, &sdr_ctrl->static_cfg); + + debug("Configuring CTRLWIDTH\n"); + writel(cfg->ctrl_width, &sdr_ctrl->ctrl_width); + + debug("Configuring PORTCFG\n"); + writel(cfg->port_cfg, &sdr_ctrl->port_cfg); + + debug("Configuring FIFOCFG\n"); + writel(cfg->fifo_cfg, &sdr_ctrl->fifo_cfg); + + debug("Configuring MPPRIORITY\n"); + writel(cfg->mp_priority, &sdr_ctrl->mp_priority); + + debug("Configuring MPWEIGHT_MPWEIGHT_0\n"); + writel(cfg->mp_weight0, &sdr_ctrl->mp_weight0); + writel(cfg->mp_weight1, &sdr_ctrl->mp_weight1); + writel(cfg->mp_weight2, &sdr_ctrl->mp_weight2); + writel(cfg->mp_weight3, &sdr_ctrl->mp_weight3); + + debug("Configuring MPPACING_MPPACING_0\n"); + writel(cfg->mp_pacing0, &sdr_ctrl->mp_pacing0); + writel(cfg->mp_pacing1, &sdr_ctrl->mp_pacing1); + writel(cfg->mp_pacing2, &sdr_ctrl->mp_pacing2); + writel(cfg->mp_pacing3, &sdr_ctrl->mp_pacing3); + + debug("Configuring MPTHRESHOLDRST_MPTHRESHOLDRST_0\n"); + writel(cfg->mp_threshold0, &sdr_ctrl->mp_threshold0); + writel(cfg->mp_threshold1, &sdr_ctrl->mp_threshold1); + writel(cfg->mp_threshold2, &sdr_ctrl->mp_threshold2); + + debug("Configuring PHYCTRL_PHYCTRL_0\n"); + writel(cfg->phy_ctrl0, &sdr_ctrl->phy_ctrl0); + + debug("Configuring CPORTWIDTH\n"); + writel(cfg->cport_width, &sdr_ctrl->cport_width); + + debug("Configuring CPORTWMAP\n"); + writel(cfg->cport_wmap, &sdr_ctrl->cport_wmap); + + debug("Configuring CPORTRMAP\n"); + writel(cfg->cport_rmap, &sdr_ctrl->cport_rmap); + + debug("Configuring RFIFOCMAP\n"); + writel(cfg->rfifo_cmap, &sdr_ctrl->rfifo_cmap); + + debug("Configuring WFIFOCMAP\n"); + writel(cfg->wfifo_cmap, &sdr_ctrl->wfifo_cmap); + + debug("Configuring CPORTRDWR\n"); + writel(cfg->cport_rdwr, &sdr_ctrl->cport_rdwr); + + debug("Configuring DRAMODT\n"); + writel(cfg->dram_odt, &sdr_ctrl->dram_odt); + + if (dram_is_ddr(3)) { + debug("Configuring EXTRATIME1\n"); + writel(cfg->extratime1, &sdr_ctrl->extratime1); + } +} + +/** + * sdram_mmr_init_full() - Function to initialize SDRAM MMR + * @sdr_phy_reg: Value of the PHY control register 0 + * + * Initialize the SDRAM MMR. + */ +int sdram_mmr_init_full(struct socfpga_sdr_ctrl *sdr_ctrl, + unsigned int sdr_phy_reg) +{ + const struct socfpga_sdram_config *cfg = socfpga_get_sdram_config(); + const unsigned int rows = + (cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB; + int ret; + + writel(rows, + socfpga_get_sysmgr_addr() + SYSMGR_ISWGRP_HANDOFF_OFFSET(4)); + + sdr_load_regs(sdr_ctrl, cfg); + + /* saving this value to SYSMGR.ISWGRP.HANDOFF.FPGA2SDR */ + writel(cfg->fpgaport_rst, + socfpga_get_sysmgr_addr() + SYSMGR_ISWGRP_HANDOFF_OFFSET(3)); + + /* only enable if the FPGA is programmed */ + if (fpgamgr_test_fpga_ready()) { + ret = sdram_write_verify(&sdr_ctrl->fpgaport_rst, + cfg->fpgaport_rst); + if (ret) + return ret; + } + + /* Restore the SDR PHY Register if valid */ + if (sdr_phy_reg != 0xffffffff) + writel(sdr_phy_reg, &sdr_ctrl->phy_ctrl0); + + /* Final step - apply configuration changes */ + debug("Configuring STATICCFG\n"); + clrsetbits_le32(&sdr_ctrl->static_cfg, + SDR_CTRLGRP_STATICCFG_APPLYCFG_MASK, + 1 << SDR_CTRLGRP_STATICCFG_APPLYCFG_LSB); + + sdram_set_protection_config(sdr_ctrl, 0, + sdram_calculate_size(sdr_ctrl) - 1); + + sdram_dump_protection_config(sdr_ctrl); + + return 0; +} + +/** + * sdram_calculate_size() - Calculate SDRAM size + * + * Calculate SDRAM device size based on SDRAM controller parameters. + * Size is specified in bytes. + */ +static unsigned long sdram_calculate_size(struct socfpga_sdr_ctrl *sdr_ctrl) +{ + unsigned long temp; + unsigned long row, bank, col, cs, width; + const struct socfpga_sdram_config *cfg = socfpga_get_sdram_config(); + const unsigned int csbits = + ((cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_CSBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_CSBITS_LSB) + 1; + const unsigned int rowbits = + (cfg->dram_addrw & SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB; + + temp = readl(&sdr_ctrl->dram_addrw); + col = (temp & SDR_CTRLGRP_DRAMADDRW_COLBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_COLBITS_LSB; + + /* + * SDRAM Failure When Accessing Non-Existent Memory + * Use ROWBITS from Quartus/QSys to calculate SDRAM size + * since the FB specifies we modify ROWBITs to work around SDRAM + * controller issue. + */ + row = readl(socfpga_get_sysmgr_addr() + + SYSMGR_ISWGRP_HANDOFF_OFFSET(4)); + if (row == 0) + row = rowbits; + /* + * If the stored handoff value for rows is greater than + * the field width in the sdr.dramaddrw register then + * something is very wrong. Revert to using the the #define + * value handed off by the SOCEDS tool chain instead of + * using a broken value. + */ + if (row > 31) + row = rowbits; + + bank = (temp & SDR_CTRLGRP_DRAMADDRW_BANKBITS_MASK) >> + SDR_CTRLGRP_DRAMADDRW_BANKBITS_LSB; + + /* + * SDRAM Failure When Accessing Non-Existent Memory + * Use CSBITs from Quartus/QSys to calculate SDRAM size + * since the FB specifies we modify CSBITs to work around SDRAM + * controller issue. + */ + cs = csbits; + + width = readl(&sdr_ctrl->dram_if_width); + + /* ECC would not be calculated as its not addressible */ + if (width == SDRAM_WIDTH_32BIT_WITH_ECC) + width = 32; + if (width == SDRAM_WIDTH_16BIT_WITH_ECC) + width = 16; + + /* calculate the SDRAM size base on this info */ + temp = 1 << (row + bank + col); + temp = temp * cs * (width / 8); + + debug("%s returns %ld\n", __func__, temp); + + return temp; +} + +static int altera_gen5_sdram_of_to_plat(struct udevice *dev) +{ + struct altera_gen5_sdram_plat *plat = dev_get_plat(dev); + + plat->sdr = (struct socfpga_sdr *)devfdt_get_addr_index(dev, 0); + if (!plat->sdr) + return -ENODEV; + + return 0; +} + +static int altera_gen5_sdram_probe(struct udevice *dev) +{ + int ret; + unsigned long sdram_size; + struct altera_gen5_sdram_plat *plat = dev_get_plat(dev); + struct altera_gen5_sdram_priv *priv = dev_get_priv(dev); + struct socfpga_sdr_ctrl *sdr_ctrl = &plat->sdr->sdr_ctrl; + struct reset_ctl_bulk resets; + + ret = reset_get_bulk(dev, &resets); + if (ret) { + dev_err(dev, "Can't get reset: %d\n", ret); + return -ENODEV; + } + reset_deassert_bulk(&resets); + + if (sdram_mmr_init_full(sdr_ctrl, 0xffffffff) != 0) { + puts("SDRAM init failed.\n"); + goto failed; + } + + debug("SDRAM: Calibrating PHY\n"); + /* SDRAM calibration */ + if (sdram_calibration_full(plat->sdr) == 0) { + puts("SDRAM calibration failed.\n"); + goto failed; + } + + sdram_size = sdram_calculate_size(sdr_ctrl); + debug("SDRAM: %ld MiB\n", sdram_size >> 20); + + /* Sanity check ensure correct SDRAM size specified */ + if (get_ram_size(0, sdram_size) != sdram_size) { + puts("SDRAM size check failed!\n"); + goto failed; + } + + priv->info.base = 0; + priv->info.size = sdram_size; + + return 0; + +failed: + reset_release_bulk(&resets); + return -ENODEV; +} + +static int altera_gen5_sdram_get_info(struct udevice *dev, + struct ram_info *info) +{ + struct altera_gen5_sdram_priv *priv = dev_get_priv(dev); + + info->base = priv->info.base; + info->size = priv->info.size; + + return 0; +} + +static const struct ram_ops altera_gen5_sdram_ops = { + .get_info = altera_gen5_sdram_get_info, +}; + +static const struct udevice_id altera_gen5_sdram_ids[] = { + { .compatible = "altr,sdr-ctl" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(altera_gen5_sdram) = { + .name = "altr_sdr_ctl", + .id = UCLASS_RAM, + .of_match = altera_gen5_sdram_ids, + .ops = &altera_gen5_sdram_ops, + .of_to_plat = altera_gen5_sdram_of_to_plat, + .plat_auto = sizeof(struct altera_gen5_sdram_plat), + .probe = altera_gen5_sdram_probe, + .priv_auto = sizeof(struct altera_gen5_sdram_priv), +}; + +#endif /* CONFIG_SPL_BUILD */ diff --git a/roms/u-boot/drivers/ddr/altera/sdram_s10.c b/roms/u-boot/drivers/ddr/altera/sdram_s10.c new file mode 100644 index 000000000..3caa2e14f --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_s10.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Intel Corporation <www.intel.com> + * + */ + +#include <common.h> +#include <cpu_func.h> +#include <dm.h> +#include <errno.h> +#include <div64.h> +#include <fdtdec.h> +#include <hang.h> +#include <init.h> +#include <log.h> +#include <ram.h> +#include <reset.h> +#include <asm/global_data.h> +#include "sdram_s10.h" +#include <wait_bit.h> +#include <asm/arch/firewall.h> +#include <asm/arch/reset_manager.h> +#include <asm/io.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; + +#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), +}; + +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; +} + +/** + * sdram_mmr_init_full() - Function to initialize SDRAM MMR + * + * Initialize the SDRAM MMR. + */ +int sdram_mmr_init_full(struct udevice *dev) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + struct altera_sdram_priv *priv = dev_get_priv(dev); + u32 update_value, io48_value, ddrioctl; + u32 i; + int ret; + phys_size_t hw_size; + struct bd_info bd = {0}; + + /* 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); + + /* Enable access to DDR from TCU */ + clrbits_le32(CCU_REG_ADDR(CCU_TCU_MPRT_ADBASE_MEMSPACE0), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_TCU_MPRT_ADBASE_MEMSPACE1A), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_TCU_MPRT_ADBASE_MEMSPACE1B), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_TCU_MPRT_ADBASE_MEMSPACE1C), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_TCU_MPRT_ADBASE_MEMSPACE1D), + CCU_ADBASE_DI_MASK); + clrbits_le32(CCU_REG_ADDR(CCU_TCU_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; + } + + /* Try 3 times to do a calibration */ + for (i = 0; i < 3; i++) { + ret = wait_for_bit_le32((const void *)(plat->hmc + + DDRCALSTAT), + DDR_HMC_DDRCALSTAT_CAL_MSK, true, 1000, + false); + if (!ret) + break; + + emif_reset(plat); + } + + if (ret) { + puts("DDR: Error as SDRAM calibration failed\n"); + return -1; + } + debug("DDR: Calibration success\n"); + + u32 ctrlcfg0 = hmc_readl(plat, CTRLCFG0); + u32 ctrlcfg1 = hmc_readl(plat, CTRLCFG1); + u32 dramaddrw = hmc_readl(plat, DRAMADDRW); + u32 dramtim0 = hmc_readl(plat, DRAMTIMING0); + u32 caltim0 = hmc_readl(plat, CALTIMING0); + u32 caltim1 = hmc_readl(plat, CALTIMING1); + u32 caltim2 = hmc_readl(plat, CALTIMING2); + u32 caltim3 = hmc_readl(plat, CALTIMING3); + u32 caltim4 = hmc_readl(plat, CALTIMING4); + u32 caltim9 = hmc_readl(plat, 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(plat, NIOSRESERVED0); + hmc_ecc_writel(plat, ((update_value & 0xFF) >> 5), DDRIOCTRL); + ddrioctl = hmc_ecc_readl(plat, DDRIOCTRL); + + /* enable HPS interface to HMC */ + hmc_ecc_writel(plat, 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(plat, update_value, DDR_SCH_DDRCONF); + + /* Configure HMC dramaddrw */ + hmc_ecc_writel(plat, hmc_readl(plat, 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(plat, ((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(plat, ((ddrioctl ? 0 : 1) << + DDR_SCH_DDRMOD_BWRATIOEXTENDED_OFF), + DDR_SCH_DDRMODE); + + /* Configure the read latency */ + ddr_sch_writel(plat, (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(plat, ((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(plat, ((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(plat); + /* If the size is invalid, use default Config size */ + if (size <= 0) + hw_size = PHYS_SDRAM_1_SIZE; + else + hw_size = size; + + /* Get bank configuration from devicetree */ + ret = fdtdec_decode_ram_size(gd->fdt_blob, NULL, 0, NULL, + (phys_size_t *)&gd->ram_size, &bd); + if (ret) { + puts("DDR: Failed to decode memory node\n"); + return -1; + } + + if (gd->ram_size != hw_size) + printf("DDR: Warning: DRAM size from device tree mismatch with hardware.\n"); + + printf("DDR: %lld MiB\n", gd->ram_size >> 20); + + /* Enable or disable the SDRAM ECC */ + if (CTRLCFG1_CFG_CTRL_EN_ECC(ctrlcfg1)) { + setbits_le32(plat->hmc + 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(plat->hmc + ECCCTRL1, + (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | + DDR_HMC_ECCCTL_CNT_RST_SET_MSK)); + setbits_le32(plat->hmc + ECCCTRL2, + (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | + DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); + hmc_ecc_writel(plat, DDR_HMC_ERRINTEN_INTMASK, ERRINTENS); + + /* Initialize memory content if not from warm reset */ + if (!cpu_has_been_warmreset()) + sdram_init_ecc_bits(&bd); + } else { + clrbits_le32(plat->hmc + 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(plat->hmc + ECCCTRL2, + (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | + DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); + } + + /* Enable non-secure reads/writes to HMC Adapter for SDRAM ECC */ + writel(FW_HMC_ADAPTOR_MPU_MASK, FW_HMC_ADAPTOR_REG_ADDR); + + sdram_size_check(&bd); + + priv->info.base = bd.bi_dram[0].start; + priv->info.size = gd->ram_size; + + debug("DDR: HMC init success\n"); + return 0; +} + diff --git a/roms/u-boot/drivers/ddr/altera/sdram_s10.h b/roms/u-boot/drivers/ddr/altera/sdram_s10.h new file mode 100644 index 000000000..cca4cb35e --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_s10.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Intel Corporation <www.intel.com> + * + */ + +#ifndef _SDRAM_S10_H_ +#define _SDRAM_S10_H_ + +#define DDR_TWR 15 +#define DDR_READ_LATENCY_DELAY 40 +#define DDR_ACTIVATE_FAWBANK 0x1 + +/* NOC DDR scheduler */ +#define DDR_SCH_ID_COREID 0 +#define DDR_SCH_ID_REVID 0x4 +#define DDR_SCH_DDRCONF 0x8 +#define DDR_SCH_DDRTIMING 0xc +#define DDR_SCH_DDRMODE 0x10 +#define DDR_SCH_READ_LATENCY 0x14 +#define DDR_SCH_ACTIVATE 0x38 +#define DDR_SCH_DEVTODEV 0x3c +#define DDR_SCH_DDR4TIMING 0x40 + +#define DDR_SCH_DDRTIMING_ACTTOACT_OFF 0 +#define DDR_SCH_DDRTIMING_RDTOMISS_OFF 6 +#define DDR_SCH_DDRTIMING_WRTOMISS_OFF 12 +#define DDR_SCH_DDRTIMING_BURSTLEN_OFF 18 +#define DDR_SCH_DDRTIMING_RDTOWR_OFF 21 +#define DDR_SCH_DDRTIMING_WRTORD_OFF 26 +#define DDR_SCH_DDRTIMING_BWRATIO_OFF 31 +#define DDR_SCH_DDRMOD_BWRATIOEXTENDED_OFF 1 +#define DDR_SCH_ACTIVATE_RRD_OFF 0 +#define DDR_SCH_ACTIVATE_FAW_OFF 4 +#define DDR_SCH_ACTIVATE_FAWBANK_OFF 10 +#define DDR_SCH_DEVTODEV_BUSRDTORD_OFF 0 +#define DDR_SCH_DEVTODEV_BUSRDTOWR_OFF 2 +#define DDR_SCH_DEVTODEV_BUSWRTORD_OFF 4 + +#include "sdram_soc64.h" + +#endif /* _SDRAM_S10_H_ */ diff --git a/roms/u-boot/drivers/ddr/altera/sdram_soc64.c b/roms/u-boot/drivers/ddr/altera/sdram_soc64.c new file mode 100644 index 000000000..a08f0953e --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_soc64.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2019 Intel Corporation <www.intel.com> + * + */ + +#include <common.h> +#include <cpu_func.h> +#include <dm.h> +#include <errno.h> +#include <div64.h> +#include <fdtdec.h> +#include <hang.h> +#include <init.h> +#include <log.h> +#include <ram.h> +#include <reset.h> +#include "sdram_soc64.h" +#include <wait_bit.h> +#include <asm/arch/firewall.h> +#include <asm/arch/system_manager.h> +#include <asm/arch/reset_manager.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/sizes.h> + +#define PGTABLE_OFF 0x4000 + +u32 hmc_readl(struct altera_sdram_plat *plat, u32 reg) +{ + return readl(plat->iomhc + reg); +} + +u32 hmc_ecc_readl(struct altera_sdram_plat *plat, u32 reg) +{ + return readl(plat->hmc + reg); +} + +u32 hmc_ecc_writel(struct altera_sdram_plat *plat, + u32 data, u32 reg) +{ + return writel(data, plat->hmc + reg); +} + +u32 ddr_sch_writel(struct altera_sdram_plat *plat, u32 data, + u32 reg) +{ + return writel(data, plat->ddr_sch + reg); +} + +int emif_clear(struct altera_sdram_plat *plat) +{ + hmc_ecc_writel(plat, 0, RSTHANDSHAKECTRL); + + return wait_for_bit_le32((const void *)(plat->hmc + + RSTHANDSHAKESTAT), + DDR_HMC_RSTHANDSHAKE_MASK, + false, 1000, false); +} + +int emif_reset(struct altera_sdram_plat *plat) +{ + u32 c2s, s2c, ret; + + c2s = hmc_ecc_readl(plat, RSTHANDSHAKECTRL) & DDR_HMC_RSTHANDSHAKE_MASK; + s2c = hmc_ecc_readl(plat, RSTHANDSHAKESTAT) & DDR_HMC_RSTHANDSHAKE_MASK; + + debug("DDR: c2s=%08x s2c=%08x nr0=%08x nr1=%08x nr2=%08x dst=%08x\n", + c2s, s2c, hmc_readl(plat, NIOSRESERVED0), + hmc_readl(plat, NIOSRESERVED1), hmc_readl(plat, NIOSRESERVED2), + hmc_readl(plat, DRAMSTS)); + + if (s2c && emif_clear(plat)) { + printf("DDR: emif_clear() failed\n"); + return -1; + } + + debug("DDR: Triggerring emif reset\n"); + hmc_ecc_writel(plat, DDR_HMC_CORE2SEQ_INT_REQ, RSTHANDSHAKECTRL); + + /* if seq2core[3] = 0, we are good */ + ret = wait_for_bit_le32((const void *)(plat->hmc + + 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(plat); + if (ret) { + printf("DDR: emif_clear() failed\n"); + return ret; + } + + debug("DDR: %s triggered successly\n", __func__); + return 0; +} + +int poll_hmc_clock_status(void) +{ + return wait_for_bit_le32((const void *)(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_HMC_CLK), + SYSMGR_HMC_CLK_STATUS_MSK, true, 1000, false); +} + +void sdram_clear_mem(phys_addr_t addr, phys_size_t size) +{ + phys_size_t i; + + if (addr % CONFIG_SYS_CACHELINE_SIZE) { + printf("DDR: address 0x%llx is not cacheline size aligned.\n", + addr); + hang(); + } + + if (size % CONFIG_SYS_CACHELINE_SIZE) { + printf("DDR: size 0x%llx is not multiple of cacheline size\n", + size); + hang(); + } + + /* Use DC ZVA instruction to clear memory to zeros by a cache line */ + for (i = 0; i < size; i = i + CONFIG_SYS_CACHELINE_SIZE) { + asm volatile("dc zva, %0" + : + : "r"(addr) + : "memory"); + addr += CONFIG_SYS_CACHELINE_SIZE; + } +} + +void sdram_init_ecc_bits(struct bd_info *bd) +{ + phys_size_t size, size_init; + phys_addr_t start_addr; + int bank = 0; + unsigned int start = get_timer(0); + + icache_enable(); + + start_addr = bd->bi_dram[0].start; + size = bd->bi_dram[0].size; + + /* Initialize small block for page table */ + memset((void *)start_addr, 0, PGTABLE_SIZE + PGTABLE_OFF); + gd->arch.tlb_addr = start_addr + PGTABLE_OFF; + gd->arch.tlb_size = PGTABLE_SIZE; + start_addr += PGTABLE_SIZE + PGTABLE_OFF; + size -= (PGTABLE_OFF + PGTABLE_SIZE); + dcache_enable(); + + while (1) { + while (size) { + size_init = min((phys_addr_t)SZ_1G, (phys_addr_t)size); + sdram_clear_mem(start_addr, size_init); + size -= size_init; + start_addr += size_init; + WATCHDOG_RESET(); + } + + bank++; + if (bank >= CONFIG_NR_DRAM_BANKS) + break; + + start_addr = bd->bi_dram[bank].start; + size = bd->bi_dram[bank].size; + } + + dcache_disable(); + icache_disable(); + + printf("SDRAM-ECC: Initialized success with %d ms\n", + (unsigned int)get_timer(start)); +} + +void sdram_size_check(struct bd_info *bd) +{ + phys_size_t total_ram_check = 0; + phys_size_t ram_check = 0; + phys_addr_t start = 0; + int bank; + + /* Sanity check ensure correct SDRAM size specified */ + debug("DDR: Running SDRAM size sanity check\n"); + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + start = bd->bi_dram[bank].start; + while (ram_check < bd->bi_dram[bank].size) { + ram_check += get_ram_size((void *)(start + ram_check), + (phys_size_t)SZ_1G); + } + total_ram_check += ram_check; + ram_check = 0; + } + + /* If the ram_size is 2GB smaller, we can assume the IO space is + * not mapped in. gd->ram_size is the actual size of the dram + * not the accessible size. + */ + if (total_ram_check != gd->ram_size) { + puts("DDR: SDRAM size check failed!\n"); + hang(); + } + + debug("DDR: SDRAM size check passed!\n"); +} + +/** + * sdram_calculate_size() - Calculate SDRAM size + * + * Calculate SDRAM device size based on SDRAM controller parameters. + * Size is specified in bytes. + */ +phys_size_t sdram_calculate_size(struct altera_sdram_plat *plat) +{ + u32 dramaddrw = hmc_readl(plat, DRAMADDRW); + + phys_size_t 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(plat, DDRIOCTRL) & + DDR_HMC_DDRIOCTRL_IOSIZE_MSK)); + + return size; +} + +static int altera_sdram_of_to_plat(struct udevice *dev) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + fdt_addr_t addr; + + addr = dev_read_addr_index(dev, 0); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->ddr_sch = (void __iomem *)addr; + + addr = dev_read_addr_index(dev, 1); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->iomhc = (void __iomem *)addr; + + addr = dev_read_addr_index(dev, 2); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->hmc = (void __iomem *)addr; + + return 0; +} + +static int altera_sdram_probe(struct udevice *dev) +{ + int ret; + struct altera_sdram_priv *priv = dev_get_priv(dev); + + ret = reset_get_bulk(dev, &priv->resets); + if (ret) { + dev_err(dev, "Can't get reset: %d\n", ret); + return -ENODEV; + } + reset_deassert_bulk(&priv->resets); + + if (sdram_mmr_init_full(dev) != 0) { + puts("SDRAM init failed.\n"); + goto failed; + } + + return 0; + +failed: + reset_release_bulk(&priv->resets); + return -ENODEV; +} + +static int altera_sdram_get_info(struct udevice *dev, + struct ram_info *info) +{ + struct altera_sdram_priv *priv = dev_get_priv(dev); + + info->base = priv->info.base; + info->size = priv->info.size; + + return 0; +} + +static struct ram_ops altera_sdram_ops = { + .get_info = altera_sdram_get_info, +}; + +static const struct udevice_id altera_sdram_ids[] = { + { .compatible = "altr,sdr-ctl-s10" }, + { .compatible = "intel,sdr-ctl-agilex" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(altera_sdram) = { + .name = "altr_sdr_ctl", + .id = UCLASS_RAM, + .of_match = altera_sdram_ids, + .ops = &altera_sdram_ops, + .of_to_plat = altera_sdram_of_to_plat, + .plat_auto = sizeof(struct altera_sdram_plat), + .probe = altera_sdram_probe, + .priv_auto = sizeof(struct altera_sdram_priv), +}; diff --git a/roms/u-boot/drivers/ddr/altera/sdram_soc64.h b/roms/u-boot/drivers/ddr/altera/sdram_soc64.h new file mode 100644 index 000000000..8af0afc41 --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sdram_soc64.h @@ -0,0 +1,187 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017-2019 Intel Corporation <www.intel.com> + */ + +#ifndef _SDRAM_SOC64_H_ +#define _SDRAM_SOC64_H_ + +#include <common.h> +#include <linux/sizes.h> + +struct altera_sdram_priv { + struct ram_info info; + struct reset_ctl_bulk resets; +}; + +struct altera_sdram_plat { + void __iomem *hmc; + void __iomem *ddr_sch; + void __iomem *iomhc; +}; + +/* ECC HMC registers */ +#define DDRIOCTRL 0x8 +#define DDRCALSTAT 0xc +#define DRAMADDRWIDTH 0xe0 +#define ECCCTRL1 0x100 +#define ECCCTRL2 0x104 +#define ERRINTEN 0x110 +#define ERRINTENS 0x114 +#define INTMODE 0x11c +#define INTSTAT 0x120 +#define AUTOWB_CORRADDR 0x138 +#define ECC_REG2WRECCDATABUS 0x144 +#define ECC_DIAGON 0x150 +#define ECC_DECSTAT 0x154 +#define HPSINTFCSEL 0x210 +#define RSTHANDSHAKECTRL 0x214 +#define RSTHANDSHAKESTAT 0x218 + +#define DDR_HMC_DDRIOCTRL_IOSIZE_MSK 0x00000003 +#define DDR_HMC_DDRCALSTAT_CAL_MSK BIT(0) +#define DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK BIT(16) +#define DDR_HMC_ECCCTL_CNT_RST_SET_MSK BIT(8) +#define DDR_HMC_ECCCTL_ECC_EN_SET_MSK BIT(0) +#define DDR_HMC_ECCCTL2_RMW_EN_SET_MSK BIT(8) +#define DDR_HMC_ECCCTL2_AWB_EN_SET_MSK BIT(0) +#define DDR_HMC_ECC_DIAGON_ECCDIAGON_EN_SET_MSK BIT(16) +#define DDR_HMC_ECC_DIAGON_WRDIAGON_EN_SET_MSK BIT(0) +#define DDR_HMC_ERRINTEN_SERRINTEN_EN_SET_MSK BIT(0) +#define DDR_HMC_ERRINTEN_DERRINTEN_EN_SET_MSK BIT(1) +#define DDR_HMC_INTSTAT_SERRPENA_SET_MSK BIT(0) +#define DDR_HMC_INTSTAT_DERRPENA_SET_MSK BIT(1) +#define DDR_HMC_INTSTAT_ADDRMTCFLG_SET_MSK BIT(16) +#define DDR_HMC_INTMODE_INTMODE_SET_MSK BIT(0) +#define DDR_HMC_RSTHANDSHAKE_MASK 0x000000ff +#define DDR_HMC_CORE2SEQ_INT_REQ 0xF +#define DDR_HMC_SEQ2CORE_INT_RESP_MASK BIT(3) +#define DDR_HMC_HPSINTFCSEL_ENABLE_MASK 0x001f1f1f + +#define DDR_HMC_ERRINTEN_INTMASK \ + (DDR_HMC_ERRINTEN_SERRINTEN_EN_SET_MSK | \ + DDR_HMC_ERRINTEN_DERRINTEN_EN_SET_MSK) + +/* HMC MMR IO48 registers */ +#define CTRLCFG0 0x28 +#define CTRLCFG1 0x2c +#define CTRLCFG3 0x34 +#define DRAMTIMING0 0x50 +#define CALTIMING0 0x7c +#define CALTIMING1 0x80 +#define CALTIMING2 0x84 +#define CALTIMING3 0x88 +#define CALTIMING4 0x8c +#define CALTIMING9 0xa0 +#define DRAMADDRW 0xa8 +#define DRAMSTS 0xec +#define NIOSRESERVED0 0x110 +#define NIOSRESERVED1 0x114 +#define NIOSRESERVED2 0x118 + +#define DRAMADDRW_CFG_COL_ADDR_WIDTH(x) \ + (((x) >> 0) & 0x1F) +#define DRAMADDRW_CFG_ROW_ADDR_WIDTH(x) \ + (((x) >> 5) & 0x1F) +#define DRAMADDRW_CFG_BANK_ADDR_WIDTH(x) \ + (((x) >> 10) & 0xF) +#define DRAMADDRW_CFG_BANK_GRP_ADDR_WIDTH(x) \ + (((x) >> 14) & 0x3) +#define DRAMADDRW_CFG_CS_ADDR_WIDTH(x) \ + (((x) >> 16) & 0x7) + +#define CTRLCFG0_CFG_MEMTYPE(x) \ + (((x) >> 0) & 0xF) +#define CTRLCFG0_CFG_DIMM_TYPE(x) \ + (((x) >> 4) & 0x7) +#define CTRLCFG0_CFG_AC_POS(x) \ + (((x) >> 7) & 0x3) +#define CTRLCFG0_CFG_CTRL_BURST_LEN(x) \ + (((x) >> 9) & 0x1F) + +#define CTRLCFG1_CFG_DBC3_BURST_LEN(x) \ + (((x) >> 0) & 0x1F) +#define CTRLCFG1_CFG_ADDR_ORDER(x) \ + (((x) >> 5) & 0x3) +#define CTRLCFG1_CFG_CTRL_EN_ECC(x) \ + (((x) >> 7) & 0x1) + +#define DRAMTIMING0_CFG_TCL(x) \ + (((x) >> 0) & 0x7F) + +#define CALTIMING0_CFG_ACT_TO_RDWR(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING0_CFG_ACT_TO_PCH(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING0_CFG_ACT_TO_ACT(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING0_CFG_ACT_TO_ACT_DB(x) \ + (((x) >> 18) & 0x3F) + +#define CALTIMING1_CFG_RD_TO_RD(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING1_CFG_RD_TO_RD_DC(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING1_CFG_RD_TO_RD_DB(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING1_CFG_RD_TO_WR(x) \ + (((x) >> 18) & 0x3F) +#define CALTIMING1_CFG_RD_TO_WR_DC(x) \ + (((x) >> 24) & 0x3F) + +#define CALTIMING2_CFG_RD_TO_WR_DB(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING2_CFG_RD_TO_WR_PCH(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING2_CFG_RD_AP_TO_VALID(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING2_CFG_WR_TO_WR(x) \ + (((x) >> 18) & 0x3F) +#define CALTIMING2_CFG_WR_TO_WR_DC(x) \ + (((x) >> 24) & 0x3F) + +#define CALTIMING3_CFG_WR_TO_WR_DB(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING3_CFG_WR_TO_RD(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING3_CFG_WR_TO_RD_DC(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING3_CFG_WR_TO_RD_DB(x) \ + (((x) >> 18) & 0x3F) +#define CALTIMING3_CFG_WR_TO_PCH(x) \ + (((x) >> 24) & 0x3F) + +#define CALTIMING4_CFG_WR_AP_TO_VALID(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING4_CFG_PCH_TO_VALID(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING4_CFG_PCH_ALL_TO_VALID(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING4_CFG_ARF_TO_VALID(x) \ + (((x) >> 18) & 0xFF) +#define CALTIMING4_CFG_PDN_TO_VALID(x) \ + (((x) >> 26) & 0x3F) + +#define CALTIMING9_CFG_4_ACT_TO_ACT(x) \ + (((x) >> 0) & 0xFF) + +/* Firewall DDR scheduler MPFE */ +#define FW_HMC_ADAPTOR_REG_ADDR 0xf8020004 +#define FW_HMC_ADAPTOR_MPU_MASK BIT(0) + +u32 hmc_readl(struct altera_sdram_plat *plat, u32 reg); +u32 hmc_ecc_readl(struct altera_sdram_plat *plat, u32 reg); +u32 hmc_ecc_writel(struct altera_sdram_plat *plat, + u32 data, u32 reg); +u32 ddr_sch_writel(struct altera_sdram_plat *plat, u32 data, + u32 reg); +int emif_clear(struct altera_sdram_plat *plat); +int emif_reset(struct altera_sdram_plat *plat); +int poll_hmc_clock_status(void); +void sdram_clear_mem(phys_addr_t addr, phys_size_t size); +void sdram_init_ecc_bits(struct bd_info *bd); +void sdram_size_check(struct bd_info *bd); +phys_size_t sdram_calculate_size(struct altera_sdram_plat *plat); +int sdram_mmr_init_full(struct udevice *dev); + +#endif /* _SDRAM_SOC64_H_ */ diff --git a/roms/u-boot/drivers/ddr/altera/sequencer.c b/roms/u-boot/drivers/ddr/altera/sequencer.c new file mode 100644 index 000000000..6b9b2e909 --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sequencer.c @@ -0,0 +1,3991 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright Altera Corporation (C) 2012-2015 + */ + +#include <common.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/sdram.h> +#include <errno.h> +#include <hang.h> +#include "sequencer.h" + +static const struct socfpga_sdr_rw_load_manager *sdr_rw_load_mgr_regs = + (struct socfpga_sdr_rw_load_manager *) + (SDR_PHYGRP_RWMGRGRP_ADDRESS | 0x800); +static const struct socfpga_sdr_rw_load_jump_manager *sdr_rw_load_jump_mgr_regs + = (struct socfpga_sdr_rw_load_jump_manager *) + (SDR_PHYGRP_RWMGRGRP_ADDRESS | 0xC00); +static const struct socfpga_sdr_reg_file *sdr_reg_file = + (struct socfpga_sdr_reg_file *)SDR_PHYGRP_REGFILEGRP_ADDRESS; +static const struct socfpga_sdr_scc_mgr *sdr_scc_mgr = + (struct socfpga_sdr_scc_mgr *) + (SDR_PHYGRP_SCCGRP_ADDRESS | 0xe00); +static const struct socfpga_phy_mgr_cmd *phy_mgr_cmd = + (struct socfpga_phy_mgr_cmd *)SDR_PHYGRP_PHYMGRGRP_ADDRESS; +static const struct socfpga_phy_mgr_cfg *phy_mgr_cfg = + (struct socfpga_phy_mgr_cfg *) + (SDR_PHYGRP_PHYMGRGRP_ADDRESS | 0x40); +static const struct socfpga_data_mgr *data_mgr = + (struct socfpga_data_mgr *)SDR_PHYGRP_DATAMGRGRP_ADDRESS; +static const struct socfpga_sdr_ctrl *sdr_ctrl = + (struct socfpga_sdr_ctrl *)SDR_CTRLGRP_ADDRESS; + +#define DELTA_D 1 + +/* + * In order to reduce ROM size, most of the selectable calibration steps are + * decided at compile time based on the user's calibration mode selection, + * as captured by the STATIC_CALIB_STEPS selection below. + * + * However, to support simulation-time selection of fast simulation mode, where + * we skip everything except the bare minimum, we need a few of the steps to + * be dynamic. In those cases, we either use the DYNAMIC_CALIB_STEPS for the + * check, which is based on the rtl-supplied value, or we dynamically compute + * the value to use based on the dynamically-chosen calibration mode + */ + +#define DLEVEL 0 +#define STATIC_IN_RTL_SIM 0 +#define STATIC_SKIP_DELAY_LOOPS 0 + +#define STATIC_CALIB_STEPS (STATIC_IN_RTL_SIM | CALIB_SKIP_FULL_TEST | \ + STATIC_SKIP_DELAY_LOOPS) + +#define SKIP_DELAY_LOOP_VALUE_OR_ZERO(non_skip_value) \ + ((non_skip_value) & seq->skip_delay_mask) + +bool dram_is_ddr(const u8 ddr) +{ + const struct socfpga_sdram_config *cfg = socfpga_get_sdram_config(); + const u8 type = (cfg->ctrl_cfg >> SDR_CTRLGRP_CTRLCFG_MEMTYPE_LSB) & + SDR_CTRLGRP_CTRLCFG_MEMTYPE_MASK; + + if (ddr == 2 && type == 1) /* DDR2 */ + return true; + + if (ddr == 3 && type == 2) /* DDR3 */ + return true; + + return false; +} + +static void set_failing_group_stage(struct socfpga_sdrseq *seq, + u32 group, u32 stage, u32 substage) +{ + /* + * Only set the global stage if there was not been any other + * failing group + */ + if (seq->gbl.error_stage == CAL_STAGE_NIL) { + seq->gbl.error_substage = substage; + seq->gbl.error_stage = stage; + seq->gbl.error_group = group; + } +} + +static void reg_file_set_group(u16 set_group) +{ + clrsetbits_le32(&sdr_reg_file->cur_stage, 0xffff0000, set_group << 16); +} + +static void reg_file_set_stage(u8 set_stage) +{ + clrsetbits_le32(&sdr_reg_file->cur_stage, 0xffff, set_stage & 0xff); +} + +static void reg_file_set_sub_stage(u8 set_sub_stage) +{ + set_sub_stage &= 0xff; + clrsetbits_le32(&sdr_reg_file->cur_stage, 0xff00, set_sub_stage << 8); +} + +/** + * phy_mgr_initialize() - Initialize PHY Manager + * + * Initialize PHY Manager. + */ +static void phy_mgr_initialize(struct socfpga_sdrseq *seq) +{ + u32 ratio; + + debug("%s:%d\n", __func__, __LINE__); + /* Calibration has control over path to memory */ + /* + * In Hard PHY this is a 2-bit control: + * 0: AFI Mux Select + * 1: DDIO Mux Select + */ + writel(0x3, &phy_mgr_cfg->mux_sel); + + /* USER memory clock is not stable we begin initialization */ + writel(0, &phy_mgr_cfg->reset_mem_stbl); + + /* USER calibration status all set to zero */ + writel(0, &phy_mgr_cfg->cal_status); + + writel(0, &phy_mgr_cfg->cal_debug_info); + + /* Init params only if we do NOT skip calibration. */ + if ((seq->dyn_calib_steps & CALIB_SKIP_ALL) == CALIB_SKIP_ALL) + return; + + ratio = seq->rwcfg->mem_dq_per_read_dqs / + seq->rwcfg->mem_virtual_groups_per_read_dqs; + seq->param.read_correct_mask_vg = (1 << ratio) - 1; + seq->param.write_correct_mask_vg = (1 << ratio) - 1; + seq->param.read_correct_mask = (1 << seq->rwcfg->mem_dq_per_read_dqs) + - 1; + seq->param.write_correct_mask = (1 << seq->rwcfg->mem_dq_per_write_dqs) + - 1; +} + +/** + * set_rank_and_odt_mask() - Set Rank and ODT mask + * @rank: Rank mask + * @odt_mode: ODT mode, OFF or READ_WRITE + * + * Set Rank and ODT mask (On-Die Termination). + */ +static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq, + const u32 rank, const u32 odt_mode) +{ + u32 odt_mask_0 = 0; + u32 odt_mask_1 = 0; + u32 cs_and_odt_mask; + + if (odt_mode == RW_MGR_ODT_MODE_OFF) { + odt_mask_0 = 0x0; + odt_mask_1 = 0x0; + } else { /* RW_MGR_ODT_MODE_READ_WRITE */ + switch (seq->rwcfg->mem_number_of_ranks) { + case 1: /* 1 Rank */ + /* Read: ODT = 0 ; Write: ODT = 1 */ + odt_mask_0 = 0x0; + odt_mask_1 = 0x1; + break; + case 2: /* 2 Ranks */ + if (seq->rwcfg->mem_number_of_cs_per_dimm == 1) { + /* + * - Dual-Slot , Single-Rank (1 CS per DIMM) + * OR + * - RDIMM, 4 total CS (2 CS per DIMM, 2 DIMM) + * + * Since MEM_NUMBER_OF_RANKS is 2, they + * are both single rank with 2 CS each + * (special for RDIMM). + * + * Read: Turn on ODT on the opposite rank + * Write: Turn on ODT on all ranks + */ + odt_mask_0 = 0x3 & ~(1 << rank); + odt_mask_1 = 0x3; + if (dram_is_ddr(2)) + odt_mask_1 &= ~(1 << rank); + } else { + /* + * - Single-Slot , Dual-Rank (2 CS per DIMM) + * + * Read: Turn on ODT off on all ranks + * Write: Turn on ODT on active rank + */ + odt_mask_0 = 0x0; + odt_mask_1 = 0x3 & (1 << rank); + } + break; + case 4: /* 4 Ranks */ + /* + * DDR3 Read, DDR2 Read/Write: + * ----------+-----------------------+ + * | ODT | + * +-----------------------+ + * Rank | 3 | 2 | 1 | 0 | + * ----------+-----+-----+-----+-----+ + * 0 | 0 | 1 | 0 | 0 | + * 1 | 1 | 0 | 0 | 0 | + * 2 | 0 | 0 | 0 | 1 | + * 3 | 0 | 0 | 1 | 0 | + * ----------+-----+-----+-----+-----+ + * + * DDR3 Write: + * ----------+-----------------------+ + * | ODT | + * Write To +-----------------------+ + * Rank | 3 | 2 | 1 | 0 | + * ----------+-----+-----+-----+-----+ + * 0 | 0 | 1 | 0 | 1 | + * 1 | 1 | 0 | 1 | 0 | + * 2 | 0 | 1 | 0 | 1 | + * 3 | 1 | 0 | 1 | 0 | + * ----------+-----+-----+-----+-----+ + */ + switch (rank) { + case 0: + odt_mask_0 = 0x4; + if (dram_is_ddr(2)) + odt_mask_1 = 0x4; + else if (dram_is_ddr(3)) + odt_mask_1 = 0x5; + break; + case 1: + odt_mask_0 = 0x8; + if (dram_is_ddr(2)) + odt_mask_1 = 0x8; + else if (dram_is_ddr(3)) + odt_mask_1 = 0xA; + break; + case 2: + odt_mask_0 = 0x1; + if (dram_is_ddr(2)) + odt_mask_1 = 0x1; + else if (dram_is_ddr(3)) + odt_mask_1 = 0x5; + break; + case 3: + odt_mask_0 = 0x2; + if (dram_is_ddr(2)) + odt_mask_1 = 0x2; + else if (dram_is_ddr(3)) + odt_mask_1 = 0xA; + break; + } + break; + } + } + + cs_and_odt_mask = (0xFF & ~(1 << rank)) | + ((0xFF & odt_mask_0) << 8) | + ((0xFF & odt_mask_1) << 16); + writel(cs_and_odt_mask, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_SET_CS_AND_ODT_MASK_OFFSET); +} + +/** + * scc_mgr_set() - Set SCC Manager register + * @off: Base offset in SCC Manager space + * @grp: Read/Write group + * @val: Value to be set + * + * This function sets the SCC Manager (Scan Chain Control Manager) register. + */ +static void scc_mgr_set(u32 off, u32 grp, u32 val) +{ + writel(val, SDR_PHYGRP_SCCGRP_ADDRESS | off | (grp << 2)); +} + +/** + * scc_mgr_initialize() - Initialize SCC Manager registers + * + * Initialize SCC Manager registers. + */ +static void scc_mgr_initialize(void) +{ + /* + * Clear register file for HPS. 16 (2^4) is the size of the + * full register file in the scc mgr: + * RFILE_DEPTH = 1 + log2(MEM_DQ_PER_DQS + 1 + MEM_DM_PER_DQS + + * MEM_IF_READ_DQS_WIDTH - 1); + */ + int i; + + for (i = 0; i < 16; i++) { + debug_cond(DLEVEL >= 1, "%s:%d: Clearing SCC RFILE index %u\n", + __func__, __LINE__, i); + scc_mgr_set(SCC_MGR_HHP_RFILE_OFFSET, i, 0); + } +} + +static void scc_mgr_set_dqdqs_output_phase(u32 write_group, u32 phase) +{ + scc_mgr_set(SCC_MGR_DQDQS_OUT_PHASE_OFFSET, write_group, phase); +} + +static void scc_mgr_set_dqs_bus_in_delay(u32 read_group, u32 delay) +{ + scc_mgr_set(SCC_MGR_DQS_IN_DELAY_OFFSET, read_group, delay); +} + +static void scc_mgr_set_dqs_en_phase(u32 read_group, u32 phase) +{ + scc_mgr_set(SCC_MGR_DQS_EN_PHASE_OFFSET, read_group, phase); +} + +static void scc_mgr_set_dqs_en_delay(u32 read_group, u32 delay) +{ + scc_mgr_set(SCC_MGR_DQS_EN_DELAY_OFFSET, read_group, delay); +} + +static void scc_mgr_set_dq_in_delay(u32 dq_in_group, u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_IN_DELAY_OFFSET, dq_in_group, delay); +} + +static void scc_mgr_set_dqs_io_in_delay(struct socfpga_sdrseq *seq, + u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_IN_DELAY_OFFSET, + seq->rwcfg->mem_dq_per_write_dqs, delay); +} + +static void scc_mgr_set_dm_in_delay(struct socfpga_sdrseq *seq, u32 dm, + u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_IN_DELAY_OFFSET, + seq->rwcfg->mem_dq_per_write_dqs + 1 + dm, + delay); +} + +static void scc_mgr_set_dq_out1_delay(u32 dq_in_group, u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_OUT1_DELAY_OFFSET, dq_in_group, delay); +} + +static void scc_mgr_set_dqs_out1_delay(struct socfpga_sdrseq *seq, + u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_OUT1_DELAY_OFFSET, + seq->rwcfg->mem_dq_per_write_dqs, delay); +} + +static void scc_mgr_set_dm_out1_delay(struct socfpga_sdrseq *seq, u32 dm, + u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_OUT1_DELAY_OFFSET, + seq->rwcfg->mem_dq_per_write_dqs + 1 + dm, + delay); +} + +/* load up dqs config settings */ +static void scc_mgr_load_dqs(u32 dqs) +{ + writel(dqs, &sdr_scc_mgr->dqs_ena); +} + +/* load up dqs io config settings */ +static void scc_mgr_load_dqs_io(void) +{ + writel(0, &sdr_scc_mgr->dqs_io_ena); +} + +/* load up dq config settings */ +static void scc_mgr_load_dq(u32 dq_in_group) +{ + writel(dq_in_group, &sdr_scc_mgr->dq_ena); +} + +/* load up dm config settings */ +static void scc_mgr_load_dm(u32 dm) +{ + writel(dm, &sdr_scc_mgr->dm_ena); +} + +/** + * scc_mgr_set_all_ranks() - Set SCC Manager register for all ranks + * @off: Base offset in SCC Manager space + * @grp: Read/Write group + * @val: Value to be set + * @update: If non-zero, trigger SCC Manager update for all ranks + * + * This function sets the SCC Manager (Scan Chain Control Manager) register + * and optionally triggers the SCC update for all ranks. + */ +static void scc_mgr_set_all_ranks(struct socfpga_sdrseq *seq, + const u32 off, const u32 grp, const u32 val, + const int update) +{ + u32 r; + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + scc_mgr_set(off, grp, val); + + if (update || (r == 0)) { + writel(grp, &sdr_scc_mgr->dqs_ena); + writel(0, &sdr_scc_mgr->update); + } + } +} + +static void scc_mgr_set_dqs_en_phase_all_ranks(struct socfpga_sdrseq *seq, + u32 read_group, u32 phase) +{ + /* + * USER although the h/w doesn't support different phases per + * shadow register, for simplicity our scc manager modeling + * keeps different phase settings per shadow reg, and it's + * important for us to keep them in sync to match h/w. + * for efficiency, the scan chain update should occur only + * once to sr0. + */ + scc_mgr_set_all_ranks(seq, SCC_MGR_DQS_EN_PHASE_OFFSET, + read_group, phase, 0); +} + +static void scc_mgr_set_dqdqs_output_phase_all_ranks(struct socfpga_sdrseq *seq, + u32 write_group, u32 phase) +{ + /* + * USER although the h/w doesn't support different phases per + * shadow register, for simplicity our scc manager modeling + * keeps different phase settings per shadow reg, and it's + * important for us to keep them in sync to match h/w. + * for efficiency, the scan chain update should occur only + * once to sr0. + */ + scc_mgr_set_all_ranks(seq, SCC_MGR_DQDQS_OUT_PHASE_OFFSET, + write_group, phase, 0); +} + +static void scc_mgr_set_dqs_en_delay_all_ranks(struct socfpga_sdrseq *seq, + u32 read_group, u32 delay) +{ + /* + * In shadow register mode, the T11 settings are stored in + * registers in the core, which are updated by the DQS_ENA + * signals. Not issuing the SCC_MGR_UPD command allows us to + * save lots of rank switching overhead, by calling + * select_shadow_regs_for_update with update_scan_chains + * set to 0. + */ + scc_mgr_set_all_ranks(seq, SCC_MGR_DQS_EN_DELAY_OFFSET, + read_group, delay, 1); +} + +/** + * scc_mgr_set_oct_out1_delay() - Set OCT output delay + * @write_group: Write group + * @delay: Delay value + * + * This function sets the OCT output delay in SCC manager. + */ +static void scc_mgr_set_oct_out1_delay(struct socfpga_sdrseq *seq, + const u32 write_group, const u32 delay) +{ + const int ratio = seq->rwcfg->mem_if_read_dqs_width / + seq->rwcfg->mem_if_write_dqs_width; + const int base = write_group * ratio; + int i; + /* + * Load the setting in the SCC manager + * Although OCT affects only write data, the OCT delay is controlled + * by the DQS logic block which is instantiated once per read group. + * For protocols where a write group consists of multiple read groups, + * the setting must be set multiple times. + */ + for (i = 0; i < ratio; i++) + scc_mgr_set(SCC_MGR_OCT_OUT1_DELAY_OFFSET, base + i, delay); +} + +/** + * scc_mgr_set_hhp_extras() - Set HHP extras. + * + * Load the fixed setting in the SCC manager HHP extras. + */ +static void scc_mgr_set_hhp_extras(void) +{ + /* + * Load the fixed setting in the SCC manager + * bits: 0:0 = 1'b1 - DQS bypass + * bits: 1:1 = 1'b1 - DQ bypass + * bits: 4:2 = 3'b001 - rfifo_mode + * bits: 6:5 = 2'b01 - rfifo clock_select + * bits: 7:7 = 1'b0 - separate gating from ungating setting + * bits: 8:8 = 1'b0 - separate OE from Output delay setting + */ + const u32 value = (0 << 8) | (0 << 7) | (1 << 5) | + (1 << 2) | (1 << 1) | (1 << 0); + const u32 addr = SDR_PHYGRP_SCCGRP_ADDRESS | + SCC_MGR_HHP_GLOBALS_OFFSET | + SCC_MGR_HHP_EXTRAS_OFFSET; + + debug_cond(DLEVEL >= 1, "%s:%d Setting HHP Extras\n", + __func__, __LINE__); + writel(value, addr); + debug_cond(DLEVEL >= 1, "%s:%d Done Setting HHP Extras\n", + __func__, __LINE__); +} + +/** + * scc_mgr_zero_all() - Zero all DQS config + * + * Zero all DQS config. + */ +static void scc_mgr_zero_all(struct socfpga_sdrseq *seq) +{ + int i, r; + + /* + * USER Zero all DQS config settings, across all groups and all + * shadow registers + */ + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + for (i = 0; i < seq->rwcfg->mem_if_read_dqs_width; i++) { + /* + * The phases actually don't exist on a per-rank basis, + * but there's no harm updating them several times, so + * let's keep the code simple. + */ + scc_mgr_set_dqs_bus_in_delay(i, + seq->iocfg->dqs_in_reserve + ); + scc_mgr_set_dqs_en_phase(i, 0); + scc_mgr_set_dqs_en_delay(i, 0); + } + + for (i = 0; i < seq->rwcfg->mem_if_write_dqs_width; i++) { + scc_mgr_set_dqdqs_output_phase(i, 0); + /* Arria V/Cyclone V don't have out2. */ + scc_mgr_set_oct_out1_delay(seq, i, + seq->iocfg->dqs_out_reserve); + } + } + + /* Multicast to all DQS group enables. */ + writel(0xff, &sdr_scc_mgr->dqs_ena); + writel(0, &sdr_scc_mgr->update); +} + +/** + * scc_set_bypass_mode() - Set bypass mode and trigger SCC update + * @write_group: Write group + * + * Set bypass mode and trigger SCC update. + */ +static void scc_set_bypass_mode(const u32 write_group) +{ + /* Multicast to all DQ enables. */ + writel(0xff, &sdr_scc_mgr->dq_ena); + writel(0xff, &sdr_scc_mgr->dm_ena); + + /* Update current DQS IO enable. */ + writel(0, &sdr_scc_mgr->dqs_io_ena); + + /* Update the DQS logic. */ + writel(write_group, &sdr_scc_mgr->dqs_ena); + + /* Hit update. */ + writel(0, &sdr_scc_mgr->update); +} + +/** + * scc_mgr_load_dqs_for_write_group() - Load DQS settings for Write Group + * @write_group: Write group + * + * Load DQS settings for Write Group, do not trigger SCC update. + */ +static void scc_mgr_load_dqs_for_write_group(struct socfpga_sdrseq *seq, + const u32 write_group) +{ + const int ratio = seq->rwcfg->mem_if_read_dqs_width / + seq->rwcfg->mem_if_write_dqs_width; + const int base = write_group * ratio; + int i; + /* + * Load the setting in the SCC manager + * Although OCT affects only write data, the OCT delay is controlled + * by the DQS logic block which is instantiated once per read group. + * For protocols where a write group consists of multiple read groups, + * the setting must be set multiple times. + */ + for (i = 0; i < ratio; i++) + writel(base + i, &sdr_scc_mgr->dqs_ena); +} + +/** + * scc_mgr_zero_group() - Zero all configs for a group + * + * Zero DQ, DM, DQS and OCT configs for a group. + */ +static void scc_mgr_zero_group(struct socfpga_sdrseq *seq, + const u32 write_group, const int out_only) +{ + int i, r; + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + /* Zero all DQ config settings. */ + for (i = 0; i < seq->rwcfg->mem_dq_per_write_dqs; i++) { + scc_mgr_set_dq_out1_delay(i, 0); + if (!out_only) + scc_mgr_set_dq_in_delay(i, 0); + } + + /* Multicast to all DQ enables. */ + writel(0xff, &sdr_scc_mgr->dq_ena); + + /* Zero all DM config settings. */ + for (i = 0; i < RW_MGR_NUM_DM_PER_WRITE_GROUP; i++) { + if (!out_only) + scc_mgr_set_dm_in_delay(seq, i, 0); + scc_mgr_set_dm_out1_delay(seq, i, 0); + } + + /* Multicast to all DM enables. */ + writel(0xff, &sdr_scc_mgr->dm_ena); + + /* Zero all DQS IO settings. */ + if (!out_only) + scc_mgr_set_dqs_io_in_delay(seq, 0); + + /* Arria V/Cyclone V don't have out2. */ + scc_mgr_set_dqs_out1_delay(seq, seq->iocfg->dqs_out_reserve); + scc_mgr_set_oct_out1_delay(seq, write_group, + seq->iocfg->dqs_out_reserve); + scc_mgr_load_dqs_for_write_group(seq, write_group); + + /* Multicast to all DQS IO enables (only 1 in total). */ + writel(0, &sdr_scc_mgr->dqs_io_ena); + + /* Hit update to zero everything. */ + writel(0, &sdr_scc_mgr->update); + } +} + +/* + * apply and load a particular input delay for the DQ pins in a group + * group_bgn is the index of the first dq pin (in the write group) + */ +static void scc_mgr_apply_group_dq_in_delay(struct socfpga_sdrseq *seq, + u32 group_bgn, u32 delay) +{ + u32 i, p; + + for (i = 0, p = group_bgn; i < seq->rwcfg->mem_dq_per_read_dqs; + i++, p++) { + scc_mgr_set_dq_in_delay(p, delay); + scc_mgr_load_dq(p); + } +} + +/** + * scc_mgr_apply_group_dq_out1_delay() - Apply and load an output delay for the + * DQ pins in a group + * @delay: Delay value + * + * Apply and load a particular output delay for the DQ pins in a group. + */ +static void scc_mgr_apply_group_dq_out1_delay(struct socfpga_sdrseq *seq, + const u32 delay) +{ + int i; + + for (i = 0; i < seq->rwcfg->mem_dq_per_write_dqs; i++) { + scc_mgr_set_dq_out1_delay(i, delay); + scc_mgr_load_dq(i); + } +} + +/* apply and load a particular output delay for the DM pins in a group */ +static void scc_mgr_apply_group_dm_out1_delay(struct socfpga_sdrseq *seq, + u32 delay1) +{ + u32 i; + + for (i = 0; i < RW_MGR_NUM_DM_PER_WRITE_GROUP; i++) { + scc_mgr_set_dm_out1_delay(seq, i, delay1); + scc_mgr_load_dm(i); + } +} + + +/* apply and load delay on both DQS and OCT out1 */ +static void scc_mgr_apply_group_dqs_io_and_oct_out1(struct socfpga_sdrseq *seq, + u32 write_group, u32 delay) +{ + scc_mgr_set_dqs_out1_delay(seq, delay); + scc_mgr_load_dqs_io(); + + scc_mgr_set_oct_out1_delay(seq, write_group, delay); + scc_mgr_load_dqs_for_write_group(seq, write_group); +} + +/** + * scc_mgr_apply_group_all_out_delay_add() - Apply a delay to the entire output + * side: DQ, DM, DQS, OCT + * @write_group: Write group + * @delay: Delay value + * + * Apply a delay to the entire output side: DQ, DM, DQS, OCT. + */ +static void scc_mgr_apply_group_all_out_delay_add(struct socfpga_sdrseq *seq, + const u32 write_group, + const u32 delay) +{ + u32 i, new_delay; + + /* DQ shift */ + for (i = 0; i < seq->rwcfg->mem_dq_per_write_dqs; i++) + scc_mgr_load_dq(i); + + /* DM shift */ + for (i = 0; i < RW_MGR_NUM_DM_PER_WRITE_GROUP; i++) + scc_mgr_load_dm(i); + + /* DQS shift */ + new_delay = READ_SCC_DQS_IO_OUT2_DELAY + delay; + if (new_delay > seq->iocfg->io_out2_delay_max) { + debug_cond(DLEVEL >= 1, + "%s:%d (%u, %u) DQS: %u > %d; adding %u to OUT1\n", + __func__, __LINE__, write_group, delay, new_delay, + seq->iocfg->io_out2_delay_max, + new_delay - seq->iocfg->io_out2_delay_max); + new_delay -= seq->iocfg->io_out2_delay_max; + scc_mgr_set_dqs_out1_delay(seq, new_delay); + } + + scc_mgr_load_dqs_io(); + + /* OCT shift */ + new_delay = READ_SCC_OCT_OUT2_DELAY + delay; + if (new_delay > seq->iocfg->io_out2_delay_max) { + debug_cond(DLEVEL >= 1, + "%s:%d (%u, %u) DQS: %u > %d; adding %u to OUT1\n", + __func__, __LINE__, write_group, delay, + new_delay, seq->iocfg->io_out2_delay_max, + new_delay - seq->iocfg->io_out2_delay_max); + new_delay -= seq->iocfg->io_out2_delay_max; + scc_mgr_set_oct_out1_delay(seq, write_group, new_delay); + } + + scc_mgr_load_dqs_for_write_group(seq, write_group); +} + +/** + * scc_mgr_apply_group_all_out_delay_add() - Apply a delay to the entire output + * side to all ranks + * @write_group: Write group + * @delay: Delay value + * + * Apply a delay to the entire output side (DQ, DM, DQS, OCT) to all ranks. + */ +static void +scc_mgr_apply_group_all_out_delay_add_all_ranks(struct socfpga_sdrseq *seq, + const u32 write_group, + const u32 delay) +{ + int r; + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + scc_mgr_apply_group_all_out_delay_add(seq, write_group, delay); + writel(0, &sdr_scc_mgr->update); + } +} + +/** + * set_jump_as_return() - Return instruction optimization + * + * Optimization used to recover some slots in ddr3 inst_rom could be + * applied to other protocols if we wanted to + */ +static void set_jump_as_return(struct socfpga_sdrseq *seq) +{ + /* + * To save space, we replace return with jump to special shared + * RETURN instruction so we set the counter to large value so that + * we always jump. + */ + writel(0xff, &sdr_rw_load_mgr_regs->load_cntr0); + writel(seq->rwcfg->rreturn, &sdr_rw_load_jump_mgr_regs->load_jump_add0); +} + +/** + * delay_for_n_mem_clocks() - Delay for N memory clocks + * @clocks: Length of the delay + * + * Delay for N memory clocks. + */ +static void delay_for_n_mem_clocks(struct socfpga_sdrseq *seq, + const u32 clocks) +{ + u32 afi_clocks; + u16 c_loop; + u8 inner; + u8 outer; + + debug("%s:%d: clocks=%u ... start\n", __func__, __LINE__, clocks); + + /* Scale (rounding up) to get afi clocks. */ + afi_clocks = DIV_ROUND_UP(clocks, seq->misccfg->afi_rate_ratio); + if (afi_clocks) /* Temporary underflow protection */ + afi_clocks--; + + /* + * Note, we don't bother accounting for being off a little + * bit because of a few extra instructions in outer loops. + * Note, the loops have a test at the end, and do the test + * before the decrement, and so always perform the loop + * 1 time more than the counter value + */ + c_loop = afi_clocks >> 16; + outer = c_loop ? 0xff : (afi_clocks >> 8); + inner = outer ? 0xff : afi_clocks; + + /* + * rom instructions are structured as follows: + * + * IDLE_LOOP2: jnz cntr0, TARGET_A + * IDLE_LOOP1: jnz cntr1, TARGET_B + * return + * + * so, when doing nested loops, TARGET_A is set to IDLE_LOOP2, and + * TARGET_B is set to IDLE_LOOP2 as well + * + * if we have no outer loop, though, then we can use IDLE_LOOP1 only, + * and set TARGET_B to IDLE_LOOP1 and we skip IDLE_LOOP2 entirely + * + * a little confusing, but it helps save precious space in the inst_rom + * and sequencer rom and keeps the delays more accurate and reduces + * overhead + */ + if (afi_clocks < 0x100) { + writel(SKIP_DELAY_LOOP_VALUE_OR_ZERO(inner), + &sdr_rw_load_mgr_regs->load_cntr1); + + writel(seq->rwcfg->idle_loop1, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + writel(seq->rwcfg->idle_loop1, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + } else { + writel(SKIP_DELAY_LOOP_VALUE_OR_ZERO(inner), + &sdr_rw_load_mgr_regs->load_cntr0); + + writel(SKIP_DELAY_LOOP_VALUE_OR_ZERO(outer), + &sdr_rw_load_mgr_regs->load_cntr1); + + writel(seq->rwcfg->idle_loop2, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(seq->rwcfg->idle_loop2, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + do { + writel(seq->rwcfg->idle_loop2, + SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + } while (c_loop-- != 0); + } + debug("%s:%d clocks=%u ... end\n", __func__, __LINE__, clocks); +} + +static void delay_for_n_ns(struct socfpga_sdrseq *seq, const u32 ns) +{ + delay_for_n_mem_clocks(seq, (ns * seq->misccfg->afi_clk_freq * + seq->misccfg->afi_rate_ratio) / 1000); +} + +/** + * rw_mgr_mem_init_load_regs() - Load instruction registers + * @cntr0: Counter 0 value + * @cntr1: Counter 1 value + * @cntr2: Counter 2 value + * @jump: Jump instruction value + * + * Load instruction registers. + */ +static void rw_mgr_mem_init_load_regs(struct socfpga_sdrseq *seq, + u32 cntr0, u32 cntr1, u32 cntr2, u32 jump) +{ + u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET; + + /* Load counters */ + writel(SKIP_DELAY_LOOP_VALUE_OR_ZERO(cntr0), + &sdr_rw_load_mgr_regs->load_cntr0); + writel(SKIP_DELAY_LOOP_VALUE_OR_ZERO(cntr1), + &sdr_rw_load_mgr_regs->load_cntr1); + writel(SKIP_DELAY_LOOP_VALUE_OR_ZERO(cntr2), + &sdr_rw_load_mgr_regs->load_cntr2); + + /* Load jump address */ + writel(jump, &sdr_rw_load_jump_mgr_regs->load_jump_add0); + writel(jump, &sdr_rw_load_jump_mgr_regs->load_jump_add1); + writel(jump, &sdr_rw_load_jump_mgr_regs->load_jump_add2); + + /* Execute count instruction */ + writel(jump, grpaddr); +} + +/** + * rw_mgr_mem_load_user_ddr2() - Load user calibration values for DDR2 + * @handoff: Indicate whether this is initialization or handoff phase + * + * Load user calibration values and optionally precharge the banks. + */ +static void rw_mgr_mem_load_user_ddr2(struct socfpga_sdrseq *seq, + const int handoff) +{ + u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET; + u32 r; + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; r++) { + /* set rank */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_OFF); + + /* precharge all banks ... */ + writel(seq->rwcfg->precharge_all, grpaddr); + + writel(seq->rwcfg->emr2, grpaddr); + writel(seq->rwcfg->emr3, grpaddr); + writel(seq->rwcfg->emr, grpaddr); + + if (handoff) { + writel(seq->rwcfg->mr_user, grpaddr); + continue; + } + + writel(seq->rwcfg->mr_dll_reset, grpaddr); + + writel(seq->rwcfg->precharge_all, grpaddr); + + writel(seq->rwcfg->refresh, grpaddr); + delay_for_n_ns(seq, 200); + writel(seq->rwcfg->refresh, grpaddr); + delay_for_n_ns(seq, 200); + + writel(seq->rwcfg->mr_calib, grpaddr); + writel(/*seq->rwcfg->*/0x0b, grpaddr); // EMR_OCD_ENABLE + writel(seq->rwcfg->emr, grpaddr); + delay_for_n_mem_clocks(seq, 200); + } +} + +/** + * rw_mgr_mem_load_user_ddr3() - Load user calibration values + * @fin1: Final instruction 1 + * @fin2: Final instruction 2 + * @precharge: If 1, precharge the banks at the end + * + * Load user calibration values and optionally precharge the banks. + */ +static void rw_mgr_mem_load_user_ddr3(struct socfpga_sdrseq *seq, + const u32 fin1, const u32 fin2, + const int precharge) +{ + u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET; + u32 r; + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; r++) { + /* set rank */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_OFF); + + /* precharge all banks ... */ + if (precharge) + writel(seq->rwcfg->precharge_all, grpaddr); + + /* + * USER Use Mirror-ed commands for odd ranks if address + * mirrorring is on + */ + if ((seq->rwcfg->mem_address_mirroring >> r) & 0x1) { + set_jump_as_return(seq); + writel(seq->rwcfg->mrs2_mirr, grpaddr); + delay_for_n_mem_clocks(seq, 4); + set_jump_as_return(seq); + writel(seq->rwcfg->mrs3_mirr, grpaddr); + delay_for_n_mem_clocks(seq, 4); + set_jump_as_return(seq); + writel(seq->rwcfg->mrs1_mirr, grpaddr); + delay_for_n_mem_clocks(seq, 4); + set_jump_as_return(seq); + writel(fin1, grpaddr); + } else { + set_jump_as_return(seq); + writel(seq->rwcfg->mrs2, grpaddr); + delay_for_n_mem_clocks(seq, 4); + set_jump_as_return(seq); + writel(seq->rwcfg->mrs3, grpaddr); + delay_for_n_mem_clocks(seq, 4); + set_jump_as_return(seq); + writel(seq->rwcfg->mrs1, grpaddr); + set_jump_as_return(seq); + writel(fin2, grpaddr); + } + + if (precharge) + continue; + + set_jump_as_return(seq); + writel(seq->rwcfg->zqcl, grpaddr); + + /* tZQinit = tDLLK = 512 ck cycles */ + delay_for_n_mem_clocks(seq, 512); + } +} + +/** + * rw_mgr_mem_load_user() - Load user calibration values + * @fin1: Final instruction 1 + * @fin2: Final instruction 2 + * @precharge: If 1, precharge the banks at the end + * + * Load user calibration values and optionally precharge the banks. + */ +static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq, + const u32 fin1, const u32 fin2, + const int precharge) +{ + if (dram_is_ddr(2)) + rw_mgr_mem_load_user_ddr2(seq, precharge); + else if (dram_is_ddr(3)) + rw_mgr_mem_load_user_ddr3(seq, fin1, fin2, precharge); + else + hang(); +} +/** + * rw_mgr_mem_initialize() - Initialize RW Manager + * + * Initialize RW Manager. + */ +static void rw_mgr_mem_initialize(struct socfpga_sdrseq *seq) +{ + debug("%s:%d\n", __func__, __LINE__); + + /* The reset / cke part of initialization is broadcasted to all ranks */ + if (dram_is_ddr(3)) { + writel(RW_MGR_RANK_ALL, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_SET_CS_AND_ODT_MASK_OFFSET); + } + + /* + * Here's how you load register for a loop + * Counters are located @ 0x800 + * Jump address are located @ 0xC00 + * For both, registers 0 to 3 are selected using bits 3 and 2, like + * in 0x800, 0x804, 0x808, 0x80C and 0xC00, 0xC04, 0xC08, 0xC0C + * I know this ain't pretty, but Avalon bus throws away the 2 least + * significant bits + */ + + /* Start with memory RESET activated */ + + /* tINIT = 200us */ + + /* + * 200us @ 266MHz (3.75 ns) ~ 54000 clock cycles + * If a and b are the number of iteration in 2 nested loops + * it takes the following number of cycles to complete the operation: + * number_of_cycles = ((2 + n) * a + 2) * b + * where n is the number of instruction in the inner loop + * One possible solution is n = 0 , a = 256 , b = 106 => a = FF, + * b = 6A + */ + rw_mgr_mem_init_load_regs(seq, seq->misccfg->tinit_cntr0_val, + seq->misccfg->tinit_cntr1_val, + seq->misccfg->tinit_cntr2_val, + seq->rwcfg->init_reset_0_cke_0); + + /* Indicate that memory is stable. */ + writel(1, &phy_mgr_cfg->reset_mem_stbl); + + if (dram_is_ddr(2)) { + writel(seq->rwcfg->nop, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + + /* Bring up clock enable. */ + + /* tXRP < 400 ck cycles */ + delay_for_n_ns(seq, 400); + } else if (dram_is_ddr(3)) { + /* + * transition the RESET to high + * Wait for 500us + */ + + /* + * 500us @ 266MHz (3.75 ns) ~ 134000 clock cycles + * If a and b are the number of iteration in 2 nested loops + * it takes the following number of cycles to complete the + * operation number_of_cycles = ((2 + n) * a + 2) * b + * where n is the number of instruction in the inner loop + * One possible solution is + * n = 2 , a = 131 , b = 256 => a = 83, b = FF + */ + rw_mgr_mem_init_load_regs(seq, seq->misccfg->treset_cntr0_val, + seq->misccfg->treset_cntr1_val, + seq->misccfg->treset_cntr2_val, + seq->rwcfg->init_reset_1_cke_0); + /* Bring up clock enable. */ + + /* tXRP < 250 ck cycles */ + delay_for_n_mem_clocks(seq, 250); + } + + rw_mgr_mem_load_user(seq, seq->rwcfg->mrs0_dll_reset_mirr, + seq->rwcfg->mrs0_dll_reset, 0); +} + +/** + * rw_mgr_mem_handoff() - Hand off the memory to user + * + * At the end of calibration we have to program the user settings in + * and hand off the memory to the user. + */ +static void rw_mgr_mem_handoff(struct socfpga_sdrseq *seq) +{ + rw_mgr_mem_load_user(seq, seq->rwcfg->mrs0_user_mirr, + seq->rwcfg->mrs0_user, 1); + /* + * Need to wait tMOD (12CK or 15ns) time before issuing other + * commands, but we will have plenty of NIOS cycles before actual + * handoff so its okay. + */ +} + +/** + * rw_mgr_mem_calibrate_write_test_issue() - Issue write test command + * @group: Write Group + * @use_dm: Use DM + * + * Issue write test command. Two variants are provided, one that just tests + * a write pattern and another that tests datamask functionality. + */ +static void rw_mgr_mem_calibrate_write_test_issue(struct socfpga_sdrseq *seq, + u32 group, u32 test_dm) +{ + const u32 quick_write_mode = + (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES) && + seq->misccfg->enable_super_quick_calibration; + u32 mcc_instruction; + u32 rw_wl_nop_cycles; + + /* + * Set counter and jump addresses for the right + * number of NOP cycles. + * The number of supported NOP cycles can range from -1 to infinity + * Three different cases are handled: + * + * 1. For a number of NOP cycles greater than 0, the RW Mgr looping + * mechanism will be used to insert the right number of NOPs + * + * 2. For a number of NOP cycles equals to 0, the micro-instruction + * issuing the write command will jump straight to the + * micro-instruction that turns on DQS (for DDRx), or outputs write + * data (for RLD), skipping + * the NOP micro-instruction all together + * + * 3. A number of NOP cycles equal to -1 indicates that DQS must be + * turned on in the same micro-instruction that issues the write + * command. Then we need + * to directly jump to the micro-instruction that sends out the data + * + * NOTE: Implementing this mechanism uses 2 RW Mgr jump-counters + * (2 and 3). One jump-counter (0) is used to perform multiple + * write-read operations. + * one counter left to issue this command in "multiple-group" mode + */ + + rw_wl_nop_cycles = seq->gbl.rw_wl_nop_cycles; + + if (rw_wl_nop_cycles == -1) { + /* + * CNTR 2 - We want to execute the special write operation that + * turns on DQS right away and then skip directly to the + * instruction that sends out the data. We set the counter to a + * large number so that the jump is always taken. + */ + writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2); + + /* CNTR 3 - Not used */ + if (test_dm) { + mcc_instruction = seq->rwcfg->lfsr_wr_rd_dm_bank_0_wl_1; + writel(seq->rwcfg->lfsr_wr_rd_dm_bank_0_data, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + writel(seq->rwcfg->lfsr_wr_rd_dm_bank_0_nop, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + } else { + mcc_instruction = seq->rwcfg->lfsr_wr_rd_bank_0_wl_1; + writel(seq->rwcfg->lfsr_wr_rd_bank_0_data, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + writel(seq->rwcfg->lfsr_wr_rd_bank_0_nop, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + } + } else if (rw_wl_nop_cycles == 0) { + /* + * CNTR 2 - We want to skip the NOP operation and go straight + * to the DQS enable instruction. We set the counter to a large + * number so that the jump is always taken. + */ + writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2); + + /* CNTR 3 - Not used */ + if (test_dm) { + mcc_instruction = seq->rwcfg->lfsr_wr_rd_dm_bank_0; + writel(seq->rwcfg->lfsr_wr_rd_dm_bank_0_dqs, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + } else { + mcc_instruction = seq->rwcfg->lfsr_wr_rd_bank_0; + writel(seq->rwcfg->lfsr_wr_rd_bank_0_dqs, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + } + } else { + /* + * CNTR 2 - In this case we want to execute the next instruction + * and NOT take the jump. So we set the counter to 0. The jump + * address doesn't count. + */ + writel(0x0, &sdr_rw_load_mgr_regs->load_cntr2); + writel(0x0, &sdr_rw_load_jump_mgr_regs->load_jump_add2); + + /* + * CNTR 3 - Set the nop counter to the number of cycles we + * need to loop for, minus 1. + */ + writel(rw_wl_nop_cycles - 1, &sdr_rw_load_mgr_regs->load_cntr3); + if (test_dm) { + mcc_instruction = seq->rwcfg->lfsr_wr_rd_dm_bank_0; + writel(seq->rwcfg->lfsr_wr_rd_dm_bank_0_nop, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + } else { + mcc_instruction = seq->rwcfg->lfsr_wr_rd_bank_0; + writel(seq->rwcfg->lfsr_wr_rd_bank_0_nop, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + } + } + + writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RESET_READ_DATAPATH_OFFSET); + + if (quick_write_mode) + writel(0x08, &sdr_rw_load_mgr_regs->load_cntr0); + else + writel(0x40, &sdr_rw_load_mgr_regs->load_cntr0); + + writel(mcc_instruction, &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + /* + * CNTR 1 - This is used to ensure enough time elapses + * for read data to come back. + */ + writel(0x30, &sdr_rw_load_mgr_regs->load_cntr1); + + if (test_dm) { + writel(seq->rwcfg->lfsr_wr_rd_dm_bank_0_wait, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + } else { + writel(seq->rwcfg->lfsr_wr_rd_bank_0_wait, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + } + + writel(mcc_instruction, (SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET) + + (group << 2)); +} + +/** + * rw_mgr_mem_calibrate_write_test() - Test writes, check for single/multiple + * pass + * @rank_bgn: Rank number + * @write_group: Write Group + * @use_dm: Use DM + * @all_correct: All bits must be correct in the mask + * @bit_chk: Resulting bit mask after the test + * @all_ranks: Test all ranks + * + * Test writes, can check for a single bit pass or multiple bit pass. + */ +static int +rw_mgr_mem_calibrate_write_test(struct socfpga_sdrseq *seq, + const u32 rank_bgn, const u32 write_group, + const u32 use_dm, const u32 all_correct, + u32 *bit_chk, const u32 all_ranks) +{ + const u32 rank_end = all_ranks ? + seq->rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + const u32 shift_ratio = seq->rwcfg->mem_dq_per_write_dqs / + seq->rwcfg->mem_virtual_groups_per_write_dqs; + const u32 correct_mask_vg = seq->param.write_correct_mask_vg; + + u32 tmp_bit_chk, base_rw_mgr, group; + int vg, r; + + *bit_chk = seq->param.write_correct_mask; + + for (r = rank_bgn; r < rank_end; r++) { + /* Set rank */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_READ_WRITE); + + tmp_bit_chk = 0; + for (vg = seq->rwcfg->mem_virtual_groups_per_write_dqs - 1; + vg >= 0; vg--) { + /* Reset the FIFOs to get pointers to known state. */ + writel(0, &phy_mgr_cmd->fifo_reset); + + group = write_group * + seq->rwcfg->mem_virtual_groups_per_write_dqs + + vg; + rw_mgr_mem_calibrate_write_test_issue(seq, group, + use_dm); + + base_rw_mgr = readl(SDR_PHYGRP_RWMGRGRP_ADDRESS); + tmp_bit_chk <<= shift_ratio; + tmp_bit_chk |= (correct_mask_vg & ~(base_rw_mgr)); + } + + *bit_chk &= tmp_bit_chk; + } + + set_rank_and_odt_mask(seq, 0, RW_MGR_ODT_MODE_OFF); + if (all_correct) { + debug_cond(DLEVEL >= 2, + "write_test(%u,%u,ALL) : %u == %u => %i\n", + write_group, use_dm, *bit_chk, + seq->param.write_correct_mask, + *bit_chk == seq->param.write_correct_mask); + return *bit_chk == seq->param.write_correct_mask; + } else { + debug_cond(DLEVEL >= 2, + "write_test(%u,%u,ONE) : %u != %i => %i\n", + write_group, use_dm, *bit_chk, 0, *bit_chk != 0); + return *bit_chk != 0x00; + } +} + +/** + * rw_mgr_mem_calibrate_read_test_patterns() - Read back test patterns + * @rank_bgn: Rank number + * @group: Read/Write Group + * @all_ranks: Test all ranks + * + * Performs a guaranteed read on the patterns we are going to use during a + * read test to ensure memory works. + */ +static int +rw_mgr_mem_calibrate_read_test_patterns(struct socfpga_sdrseq *seq, + const u32 rank_bgn, const u32 group, + const u32 all_ranks) +{ + const u32 addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET; + const u32 addr_offset = + (group * seq->rwcfg->mem_virtual_groups_per_read_dqs) + << 2; + const u32 rank_end = all_ranks ? + seq->rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + const u32 shift_ratio = seq->rwcfg->mem_dq_per_read_dqs / + seq->rwcfg->mem_virtual_groups_per_read_dqs; + const u32 correct_mask_vg = seq->param.read_correct_mask_vg; + + u32 tmp_bit_chk, base_rw_mgr, bit_chk; + int vg, r; + int ret = 0; + + bit_chk = seq->param.read_correct_mask; + + for (r = rank_bgn; r < rank_end; r++) { + /* Set rank */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_READ_WRITE); + + /* Load up a constant bursts of read commands */ + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr0); + writel(seq->rwcfg->guaranteed_read, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr1); + writel(seq->rwcfg->guaranteed_read_cont, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + tmp_bit_chk = 0; + for (vg = seq->rwcfg->mem_virtual_groups_per_read_dqs - 1; + vg >= 0; vg--) { + /* Reset the FIFOs to get pointers to known state. */ + writel(0, &phy_mgr_cmd->fifo_reset); + writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RESET_READ_DATAPATH_OFFSET); + writel(seq->rwcfg->guaranteed_read, + addr + addr_offset + (vg << 2)); + + base_rw_mgr = readl(SDR_PHYGRP_RWMGRGRP_ADDRESS); + tmp_bit_chk <<= shift_ratio; + tmp_bit_chk |= correct_mask_vg & ~base_rw_mgr; + } + + bit_chk &= tmp_bit_chk; + } + + writel(seq->rwcfg->clear_dqs_enable, addr + (group << 2)); + + set_rank_and_odt_mask(seq, 0, RW_MGR_ODT_MODE_OFF); + + if (bit_chk != seq->param.read_correct_mask) + ret = -EIO; + + debug_cond(DLEVEL >= 1, + "%s:%d test_load_patterns(%u,ALL) => (%u == %u) => %i\n", + __func__, __LINE__, group, bit_chk, + seq->param.read_correct_mask, ret); + + return ret; +} + +/** + * rw_mgr_mem_calibrate_read_load_patterns() - Load up the patterns for read + * test + * @rank_bgn: Rank number + * @all_ranks: Test all ranks + * + * Load up the patterns we are going to use during a read test. + */ +static void rw_mgr_mem_calibrate_read_load_patterns(struct socfpga_sdrseq *seq, + const u32 rank_bgn, + const int all_ranks) +{ + const u32 rank_end = all_ranks ? + seq->rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + u32 r; + + debug("%s:%d\n", __func__, __LINE__); + + for (r = rank_bgn; r < rank_end; r++) { + /* set rank */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_READ_WRITE); + + /* Load up a constant bursts */ + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr0); + + writel(seq->rwcfg->guaranteed_write_wait0, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr1); + + writel(seq->rwcfg->guaranteed_write_wait1, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + writel(0x04, &sdr_rw_load_mgr_regs->load_cntr2); + + writel(seq->rwcfg->guaranteed_write_wait2, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + + writel(0x04, &sdr_rw_load_mgr_regs->load_cntr3); + + writel(seq->rwcfg->guaranteed_write_wait3, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + + writel(seq->rwcfg->guaranteed_write, + SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + } + + set_rank_and_odt_mask(seq, 0, RW_MGR_ODT_MODE_OFF); +} + +/** + * rw_mgr_mem_calibrate_read_test() - Perform READ test on single rank + * @rank_bgn: Rank number + * @group: Read/Write group + * @num_tries: Number of retries of the test + * @all_correct: All bits must be correct in the mask + * @bit_chk: Resulting bit mask after the test + * @all_groups: Test all R/W groups + * @all_ranks: Test all ranks + * + * Try a read and see if it returns correct data back. Test has dummy reads + * inserted into the mix used to align DQS enable. Test has more thorough + * checks than the regular read test. + */ +static int +rw_mgr_mem_calibrate_read_test(struct socfpga_sdrseq *seq, + const u32 rank_bgn, const u32 group, + const u32 num_tries, const u32 all_correct, + u32 *bit_chk, + const u32 all_groups, const u32 all_ranks) +{ + const u32 rank_end = all_ranks ? seq->rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + const u32 quick_read_mode = + ((STATIC_CALIB_STEPS & CALIB_SKIP_DELAY_SWEEPS) && + seq->misccfg->enable_super_quick_calibration); + u32 correct_mask_vg = seq->param.read_correct_mask_vg; + u32 tmp_bit_chk; + u32 base_rw_mgr; + u32 addr; + + int r, vg, ret; + + *bit_chk = seq->param.read_correct_mask; + + for (r = rank_bgn; r < rank_end; r++) { + /* set rank */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_READ_WRITE); + + writel(0x10, &sdr_rw_load_mgr_regs->load_cntr1); + + writel(seq->rwcfg->read_b2b_wait1, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + writel(0x10, &sdr_rw_load_mgr_regs->load_cntr2); + writel(seq->rwcfg->read_b2b_wait2, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + + if (quick_read_mode) + writel(0x1, &sdr_rw_load_mgr_regs->load_cntr0); + /* need at least two (1+1) reads to capture failures */ + else if (all_groups) + writel(0x06, &sdr_rw_load_mgr_regs->load_cntr0); + else + writel(0x32, &sdr_rw_load_mgr_regs->load_cntr0); + + writel(seq->rwcfg->read_b2b, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + if (all_groups) + writel(seq->rwcfg->mem_if_read_dqs_width * + seq->rwcfg->mem_virtual_groups_per_read_dqs - 1, + &sdr_rw_load_mgr_regs->load_cntr3); + else + writel(0x0, &sdr_rw_load_mgr_regs->load_cntr3); + + writel(seq->rwcfg->read_b2b, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + + tmp_bit_chk = 0; + for (vg = seq->rwcfg->mem_virtual_groups_per_read_dqs - 1; + vg >= 0; vg--) { + /* Reset the FIFOs to get pointers to known state. */ + writel(0, &phy_mgr_cmd->fifo_reset); + writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RESET_READ_DATAPATH_OFFSET); + + if (all_groups) { + addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_ALL_GROUPS_OFFSET; + } else { + addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET; + } + + writel(seq->rwcfg->read_b2b, addr + + ((group * + seq->rwcfg->mem_virtual_groups_per_read_dqs + + vg) << 2)); + + base_rw_mgr = readl(SDR_PHYGRP_RWMGRGRP_ADDRESS); + tmp_bit_chk <<= + seq->rwcfg->mem_dq_per_read_dqs / + seq->rwcfg->mem_virtual_groups_per_read_dqs; + tmp_bit_chk |= correct_mask_vg & ~(base_rw_mgr); + } + + *bit_chk &= tmp_bit_chk; + } + + addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_RUN_SINGLE_GROUP_OFFSET; + writel(seq->rwcfg->clear_dqs_enable, addr + (group << 2)); + + set_rank_and_odt_mask(seq, 0, RW_MGR_ODT_MODE_OFF); + + if (all_correct) { + ret = (*bit_chk == seq->param.read_correct_mask); + debug_cond(DLEVEL >= 2, + "%s:%d read_test(%u,ALL,%u) => (%u == %u) => %i\n", + __func__, __LINE__, group, all_groups, *bit_chk, + seq->param.read_correct_mask, ret); + } else { + ret = (*bit_chk != 0x00); + debug_cond(DLEVEL >= 2, + "%s:%d read_test(%u,ONE,%u) => (%u != %u) => %i\n", + __func__, __LINE__, group, all_groups, *bit_chk, + 0, ret); + } + + return ret; +} + +/** + * rw_mgr_mem_calibrate_read_test_all_ranks() - Perform READ test on all ranks + * @grp: Read/Write group + * @num_tries: Number of retries of the test + * @all_correct: All bits must be correct in the mask + * @all_groups: Test all R/W groups + * + * Perform a READ test across all memory ranks. + */ +static int +rw_mgr_mem_calibrate_read_test_all_ranks(struct socfpga_sdrseq *seq, + const u32 grp, const u32 num_tries, + const u32 all_correct, + const u32 all_groups) +{ + u32 bit_chk; + return rw_mgr_mem_calibrate_read_test(seq, 0, grp, num_tries, + all_correct, &bit_chk, all_groups, + 1); +} + +/** + * rw_mgr_incr_vfifo() - Increase VFIFO value + * @grp: Read/Write group + * + * Increase VFIFO value. + */ +static void rw_mgr_incr_vfifo(const u32 grp) +{ + writel(grp, &phy_mgr_cmd->inc_vfifo_hard_phy); +} + +/** + * rw_mgr_decr_vfifo() - Decrease VFIFO value + * @grp: Read/Write group + * + * Decrease VFIFO value. + */ +static void rw_mgr_decr_vfifo(struct socfpga_sdrseq *seq, const u32 grp) +{ + u32 i; + + for (i = 0; i < seq->misccfg->read_valid_fifo_size - 1; i++) + rw_mgr_incr_vfifo(grp); +} + +/** + * find_vfifo_failing_read() - Push VFIFO to get a failing read + * @grp: Read/Write group + * + * Push VFIFO until a failing read happens. + */ +static int find_vfifo_failing_read(struct socfpga_sdrseq *seq, + const u32 grp) +{ + u32 v, ret, fail_cnt = 0; + + for (v = 0; v < seq->misccfg->read_valid_fifo_size; v++) { + debug_cond(DLEVEL >= 2, "%s:%d: vfifo %u\n", + __func__, __LINE__, v); + ret = rw_mgr_mem_calibrate_read_test_all_ranks(seq, grp, 1, + PASS_ONE_BIT, 0); + if (!ret) { + fail_cnt++; + + if (fail_cnt == 2) + return v; + } + + /* Fiddle with FIFO. */ + rw_mgr_incr_vfifo(grp); + } + + /* No failing read found! Something must have gone wrong. */ + debug_cond(DLEVEL >= 2, "%s:%d: vfifo failed\n", __func__, __LINE__); + return 0; +} + +/** + * sdr_find_phase_delay() - Find DQS enable phase or delay + * @working: If 1, look for working phase/delay, if 0, look for non-working + * @delay: If 1, look for delay, if 0, look for phase + * @grp: Read/Write group + * @work: Working window position + * @work_inc: Working window increment + * @pd: DQS Phase/Delay Iterator + * + * Find working or non-working DQS enable phase setting. + */ +static int sdr_find_phase_delay(struct socfpga_sdrseq *seq, int working, + int delay, const u32 grp, u32 *work, + const u32 work_inc, u32 *pd) +{ + const u32 max = delay ? seq->iocfg->dqs_en_delay_max : + seq->iocfg->dqs_en_phase_max; + u32 ret; + + for (; *pd <= max; (*pd)++) { + if (delay) + scc_mgr_set_dqs_en_delay_all_ranks(seq, grp, *pd); + else + scc_mgr_set_dqs_en_phase_all_ranks(seq, grp, *pd); + + ret = rw_mgr_mem_calibrate_read_test_all_ranks(seq, grp, 1, + PASS_ONE_BIT, 0); + if (!working) + ret = !ret; + + if (ret) + return 0; + + if (work) + *work += work_inc; + } + + return -EINVAL; +} +/** + * sdr_find_phase() - Find DQS enable phase + * @working: If 1, look for working phase, if 0, look for non-working phase + * @grp: Read/Write group + * @work: Working window position + * @i: Iterator + * @p: DQS Phase Iterator + * + * Find working or non-working DQS enable phase setting. + */ +static int sdr_find_phase(struct socfpga_sdrseq *seq, int working, + const u32 grp, u32 *work, u32 *i, u32 *p) +{ + const u32 end = seq->misccfg->read_valid_fifo_size + (working ? 0 : 1); + int ret; + + for (; *i < end; (*i)++) { + if (working) + *p = 0; + + ret = sdr_find_phase_delay(seq, working, 0, grp, work, + seq->iocfg->delay_per_opa_tap, p); + if (!ret) + return 0; + + if (*p > seq->iocfg->dqs_en_phase_max) { + /* Fiddle with FIFO. */ + rw_mgr_incr_vfifo(grp); + if (!working) + *p = 0; + } + } + + return -EINVAL; +} + +/** + * sdr_working_phase() - Find working DQS enable phase + * @grp: Read/Write group + * @work_bgn: Working window start position + * @d: dtaps output value + * @p: DQS Phase Iterator + * @i: Iterator + * + * Find working DQS enable phase setting. + */ +static int sdr_working_phase(struct socfpga_sdrseq *seq, const u32 grp, + u32 *work_bgn, u32 *d, u32 *p, u32 *i) +{ + const u32 dtaps_per_ptap = seq->iocfg->delay_per_opa_tap / + seq->iocfg->delay_per_dqs_en_dchain_tap; + int ret; + + *work_bgn = 0; + + for (*d = 0; *d <= dtaps_per_ptap; (*d)++) { + *i = 0; + scc_mgr_set_dqs_en_delay_all_ranks(seq, grp, *d); + ret = sdr_find_phase(seq, 1, grp, work_bgn, i, p); + if (!ret) + return 0; + *work_bgn += seq->iocfg->delay_per_dqs_en_dchain_tap; + } + + /* Cannot find working solution */ + debug_cond(DLEVEL >= 2, "%s:%d find_dqs_en_phase: no vfifo/ptap/dtap\n", + __func__, __LINE__); + return -EINVAL; +} + +/** + * sdr_backup_phase() - Find DQS enable backup phase + * @grp: Read/Write group + * @work_bgn: Working window start position + * @p: DQS Phase Iterator + * + * Find DQS enable backup phase setting. + */ +static void sdr_backup_phase(struct socfpga_sdrseq *seq, const u32 grp, + u32 *work_bgn, u32 *p) +{ + u32 tmp_delay, d; + int ret; + + /* Special case code for backing up a phase */ + if (*p == 0) { + *p = seq->iocfg->dqs_en_phase_max; + rw_mgr_decr_vfifo(seq, grp); + } else { + (*p)--; + } + tmp_delay = *work_bgn - seq->iocfg->delay_per_opa_tap; + scc_mgr_set_dqs_en_phase_all_ranks(seq, grp, *p); + + for (d = 0; d <= seq->iocfg->dqs_en_delay_max && tmp_delay < *work_bgn; + d++) { + scc_mgr_set_dqs_en_delay_all_ranks(seq, grp, d); + + ret = rw_mgr_mem_calibrate_read_test_all_ranks(seq, grp, 1, + PASS_ONE_BIT, 0); + if (ret) { + *work_bgn = tmp_delay; + break; + } + + tmp_delay += seq->iocfg->delay_per_dqs_en_dchain_tap; + } + + /* Restore VFIFO to old state before we decremented it (if needed). */ + (*p)++; + if (*p > seq->iocfg->dqs_en_phase_max) { + *p = 0; + rw_mgr_incr_vfifo(grp); + } + + scc_mgr_set_dqs_en_delay_all_ranks(seq, grp, 0); +} + +/** + * sdr_nonworking_phase() - Find non-working DQS enable phase + * @grp: Read/Write group + * @work_end: Working window end position + * @p: DQS Phase Iterator + * @i: Iterator + * + * Find non-working DQS enable phase setting. + */ +static int sdr_nonworking_phase(struct socfpga_sdrseq *seq, + const u32 grp, u32 *work_end, u32 *p, u32 *i) +{ + int ret; + + (*p)++; + *work_end += seq->iocfg->delay_per_opa_tap; + if (*p > seq->iocfg->dqs_en_phase_max) { + /* Fiddle with FIFO. */ + *p = 0; + rw_mgr_incr_vfifo(grp); + } + + ret = sdr_find_phase(seq, 0, grp, work_end, i, p); + if (ret) { + /* Cannot see edge of failing read. */ + debug_cond(DLEVEL >= 2, "%s:%d: end: failed\n", + __func__, __LINE__); + } + + return ret; +} + +/** + * sdr_find_window_center() - Find center of the working DQS window. + * @grp: Read/Write group + * @work_bgn: First working settings + * @work_end: Last working settings + * + * Find center of the working DQS enable window. + */ +static int sdr_find_window_center(struct socfpga_sdrseq *seq, + const u32 grp, const u32 work_bgn, + const u32 work_end) +{ + u32 work_mid; + int tmp_delay = 0; + int i, p, d; + + work_mid = (work_bgn + work_end) / 2; + + debug_cond(DLEVEL >= 2, "work_bgn=%d work_end=%d work_mid=%d\n", + work_bgn, work_end, work_mid); + /* Get the middle delay to be less than a VFIFO delay */ + tmp_delay = (seq->iocfg->dqs_en_phase_max + 1) + * seq->iocfg->delay_per_opa_tap; + + debug_cond(DLEVEL >= 2, "vfifo ptap delay %d\n", tmp_delay); + work_mid %= tmp_delay; + debug_cond(DLEVEL >= 2, "new work_mid %d\n", work_mid); + + tmp_delay = rounddown(work_mid, seq->iocfg->delay_per_opa_tap); + if (tmp_delay > seq->iocfg->dqs_en_phase_max + * seq->iocfg->delay_per_opa_tap) { + tmp_delay = seq->iocfg->dqs_en_phase_max + * seq->iocfg->delay_per_opa_tap; + } + p = tmp_delay / seq->iocfg->delay_per_opa_tap; + + debug_cond(DLEVEL >= 2, "new p %d, tmp_delay=%d\n", p, tmp_delay); + + d = DIV_ROUND_UP(work_mid - tmp_delay, + seq->iocfg->delay_per_dqs_en_dchain_tap); + if (d > seq->iocfg->dqs_en_delay_max) + d = seq->iocfg->dqs_en_delay_max; + tmp_delay += d * seq->iocfg->delay_per_dqs_en_dchain_tap; + + debug_cond(DLEVEL >= 2, "new d %d, tmp_delay=%d\n", d, tmp_delay); + + scc_mgr_set_dqs_en_phase_all_ranks(seq, grp, p); + scc_mgr_set_dqs_en_delay_all_ranks(seq, grp, d); + + /* + * push vfifo until we can successfully calibrate. We can do this + * because the largest possible margin in 1 VFIFO cycle. + */ + for (i = 0; i < seq->misccfg->read_valid_fifo_size; i++) { + debug_cond(DLEVEL >= 2, "find_dqs_en_phase: center\n"); + if (rw_mgr_mem_calibrate_read_test_all_ranks(seq, grp, 1, + PASS_ONE_BIT, + 0)) { + debug_cond(DLEVEL >= 2, + "%s:%d center: found: ptap=%u dtap=%u\n", + __func__, __LINE__, p, d); + return 0; + } + + /* Fiddle with FIFO. */ + rw_mgr_incr_vfifo(grp); + } + + debug_cond(DLEVEL >= 2, "%s:%d center: failed.\n", + __func__, __LINE__); + return -EINVAL; +} + +/** + * rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase() - Find a good DQS enable to + * use + * @grp: Read/Write Group + * + * Find a good DQS enable to use. + */ +static int +rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase(struct socfpga_sdrseq *seq, + const u32 grp) +{ + u32 d, p, i; + u32 dtaps_per_ptap; + u32 work_bgn, work_end; + u32 found_passing_read, found_failing_read = 0, initial_failing_dtap; + int ret; + + debug("%s:%d %u\n", __func__, __LINE__, grp); + + reg_file_set_sub_stage(CAL_SUBSTAGE_VFIFO_CENTER); + + scc_mgr_set_dqs_en_delay_all_ranks(seq, grp, 0); + scc_mgr_set_dqs_en_phase_all_ranks(seq, grp, 0); + + /* Step 0: Determine number of delay taps for each phase tap. */ + dtaps_per_ptap = seq->iocfg->delay_per_opa_tap / + seq->iocfg->delay_per_dqs_en_dchain_tap; + + /* Step 1: First push vfifo until we get a failing read. */ + find_vfifo_failing_read(seq, grp); + + /* Step 2: Find first working phase, increment in ptaps. */ + work_bgn = 0; + ret = sdr_working_phase(seq, grp, &work_bgn, &d, &p, &i); + if (ret) + return ret; + + work_end = work_bgn; + + /* + * If d is 0 then the working window covers a phase tap and we can + * follow the old procedure. Otherwise, we've found the beginning + * and we need to increment the dtaps until we find the end. + */ + if (d == 0) { + /* + * Step 3a: If we have room, back off by one and + * increment in dtaps. + */ + sdr_backup_phase(seq, grp, &work_bgn, &p); + + /* + * Step 4a: go forward from working phase to non working + * phase, increment in ptaps. + */ + ret = sdr_nonworking_phase(seq, grp, &work_end, &p, &i); + if (ret) + return ret; + + /* Step 5a: Back off one from last, increment in dtaps. */ + + /* Special case code for backing up a phase */ + if (p == 0) { + p = seq->iocfg->dqs_en_phase_max; + rw_mgr_decr_vfifo(seq, grp); + } else { + p = p - 1; + } + + work_end -= seq->iocfg->delay_per_opa_tap; + scc_mgr_set_dqs_en_phase_all_ranks(seq, grp, p); + + d = 0; + + debug_cond(DLEVEL >= 2, "%s:%d p: ptap=%u\n", + __func__, __LINE__, p); + } + + /* The dtap increment to find the failing edge is done here. */ + sdr_find_phase_delay(seq, 0, 1, grp, &work_end, + seq->iocfg->delay_per_dqs_en_dchain_tap, &d); + + /* Go back to working dtap */ + if (d != 0) + work_end -= seq->iocfg->delay_per_dqs_en_dchain_tap; + + debug_cond(DLEVEL >= 2, + "%s:%d p/d: ptap=%u dtap=%u end=%u\n", + __func__, __LINE__, p, d - 1, work_end); + + if (work_end < work_bgn) { + /* nil range */ + debug_cond(DLEVEL >= 2, "%s:%d end-2: failed\n", + __func__, __LINE__); + return -EINVAL; + } + + debug_cond(DLEVEL >= 2, "%s:%d found range [%u,%u]\n", + __func__, __LINE__, work_bgn, work_end); + + /* + * We need to calculate the number of dtaps that equal a ptap. + * To do that we'll back up a ptap and re-find the edge of the + * window using dtaps + */ + debug_cond(DLEVEL >= 2, "%s:%d calculate dtaps_per_ptap for tracking\n", + __func__, __LINE__); + + /* Special case code for backing up a phase */ + if (p == 0) { + p = seq->iocfg->dqs_en_phase_max; + rw_mgr_decr_vfifo(seq, grp); + debug_cond(DLEVEL >= 2, "%s:%d backedup cycle/phase: p=%u\n", + __func__, __LINE__, p); + } else { + p = p - 1; + debug_cond(DLEVEL >= 2, "%s:%d backedup phase only: p=%u", + __func__, __LINE__, p); + } + + scc_mgr_set_dqs_en_phase_all_ranks(seq, grp, p); + + /* + * Increase dtap until we first see a passing read (in case the + * window is smaller than a ptap), and then a failing read to + * mark the edge of the window again. + */ + + /* Find a passing read. */ + debug_cond(DLEVEL >= 2, "%s:%d find passing read\n", + __func__, __LINE__); + + initial_failing_dtap = d; + + found_passing_read = !sdr_find_phase_delay(seq, 1, 1, grp, NULL, 0, &d); + if (found_passing_read) { + /* Find a failing read. */ + debug_cond(DLEVEL >= 2, "%s:%d find failing read\n", + __func__, __LINE__); + d++; + found_failing_read = !sdr_find_phase_delay(seq, 0, 1, grp, NULL, + 0, &d); + } else { + debug_cond(DLEVEL >= 1, + "%s:%d failed to calculate dtaps per ptap. Fall back on static value\n", + __func__, __LINE__); + } + + /* + * The dynamically calculated dtaps_per_ptap is only valid if we + * found a passing/failing read. If we didn't, it means d hit the max + * (seq->iocfg->dqs_en_delay_max). Otherwise, dtaps_per_ptap retains its + * statically calculated value. + */ + if (found_passing_read && found_failing_read) + dtaps_per_ptap = d - initial_failing_dtap; + + writel(dtaps_per_ptap, &sdr_reg_file->dtaps_per_ptap); + debug_cond(DLEVEL >= 2, "%s:%d dtaps_per_ptap=%u - %u = %u", + __func__, __LINE__, d, initial_failing_dtap, dtaps_per_ptap); + + /* Step 6: Find the centre of the window. */ + ret = sdr_find_window_center(seq, grp, work_bgn, work_end); + + return ret; +} + +/** + * search_stop_check() - Check if the detected edge is valid + * @write: Perform read (Stage 2) or write (Stage 3) calibration + * @d: DQS delay + * @rank_bgn: Rank number + * @write_group: Write Group + * @read_group: Read Group + * @bit_chk: Resulting bit mask after the test + * @sticky_bit_chk: Resulting sticky bit mask after the test + * @use_read_test: Perform read test + * + * Test if the found edge is valid. + */ +static u32 search_stop_check(struct socfpga_sdrseq *seq, const int write, + const int d, const int rank_bgn, + const u32 write_group, const u32 read_group, + u32 *bit_chk, u32 *sticky_bit_chk, + const u32 use_read_test) +{ + const u32 ratio = seq->rwcfg->mem_if_read_dqs_width / + seq->rwcfg->mem_if_write_dqs_width; + const u32 correct_mask = write ? seq->param.write_correct_mask : + seq->param.read_correct_mask; + const u32 per_dqs = write ? seq->rwcfg->mem_dq_per_write_dqs : + seq->rwcfg->mem_dq_per_read_dqs; + u32 ret; + /* + * Stop searching when the read test doesn't pass AND when + * we've seen a passing read on every bit. + */ + if (write) { /* WRITE-ONLY */ + ret = !rw_mgr_mem_calibrate_write_test(seq, rank_bgn, + write_group, 0, + PASS_ONE_BIT, bit_chk, + 0); + } else if (use_read_test) { /* READ-ONLY */ + ret = !rw_mgr_mem_calibrate_read_test(seq, rank_bgn, read_group, + NUM_READ_PB_TESTS, + PASS_ONE_BIT, bit_chk, + 0, 0); + } else { /* READ-ONLY */ + rw_mgr_mem_calibrate_write_test(seq, rank_bgn, write_group, 0, + PASS_ONE_BIT, bit_chk, 0); + *bit_chk = *bit_chk >> (per_dqs * + (read_group - (write_group * ratio))); + ret = (*bit_chk == 0); + } + *sticky_bit_chk = *sticky_bit_chk | *bit_chk; + ret = ret && (*sticky_bit_chk == correct_mask); + debug_cond(DLEVEL >= 2, + "%s:%d center(left): dtap=%u => %u == %u && %u", + __func__, __LINE__, d, + *sticky_bit_chk, correct_mask, ret); + return ret; +} + +/** + * search_left_edge() - Find left edge of DQ/DQS working phase + * @write: Perform read (Stage 2) or write (Stage 3) calibration + * @rank_bgn: Rank number + * @write_group: Write Group + * @read_group: Read Group + * @test_bgn: Rank number to begin the test + * @sticky_bit_chk: Resulting sticky bit mask after the test + * @left_edge: Left edge of the DQ/DQS phase + * @right_edge: Right edge of the DQ/DQS phase + * @use_read_test: Perform read test + * + * Find left edge of DQ/DQS working phase. + */ +static void search_left_edge(struct socfpga_sdrseq *seq, const int write, + const int rank_bgn, const u32 write_group, + const u32 read_group, const u32 test_bgn, + u32 *sticky_bit_chk, int *left_edge, + int *right_edge, const u32 use_read_test) +{ + const u32 delay_max = write ? seq->iocfg->io_out1_delay_max : + seq->iocfg->io_in_delay_max; + const u32 dqs_max = write ? seq->iocfg->io_out1_delay_max : + seq->iocfg->dqs_in_delay_max; + const u32 per_dqs = write ? seq->rwcfg->mem_dq_per_write_dqs : + seq->rwcfg->mem_dq_per_read_dqs; + u32 stop, bit_chk; + int i, d; + + for (d = 0; d <= dqs_max; d++) { + if (write) + scc_mgr_apply_group_dq_out1_delay(seq, d); + else + scc_mgr_apply_group_dq_in_delay(seq, test_bgn, d); + + writel(0, &sdr_scc_mgr->update); + + stop = search_stop_check(seq, write, d, rank_bgn, write_group, + read_group, &bit_chk, sticky_bit_chk, + use_read_test); + if (stop == 1) + break; + + /* stop != 1 */ + for (i = 0; i < per_dqs; i++) { + if (bit_chk & 1) { + /* + * Remember a passing test as + * the left_edge. + */ + left_edge[i] = d; + } else { + /* + * If a left edge has not been seen + * yet, then a future passing test + * will mark this edge as the right + * edge. + */ + if (left_edge[i] == delay_max + 1) + right_edge[i] = -(d + 1); + } + bit_chk >>= 1; + } + } + + /* Reset DQ delay chains to 0 */ + if (write) + scc_mgr_apply_group_dq_out1_delay(seq, 0); + else + scc_mgr_apply_group_dq_in_delay(seq, test_bgn, 0); + + *sticky_bit_chk = 0; + for (i = per_dqs - 1; i >= 0; i--) { + debug_cond(DLEVEL >= 2, + "%s:%d vfifo_center: left_edge[%u]: %d right_edge[%u]: %d\n", + __func__, __LINE__, i, left_edge[i], + i, right_edge[i]); + + /* + * Check for cases where we haven't found the left edge, + * which makes our assignment of the the right edge invalid. + * Reset it to the illegal value. + */ + if ((left_edge[i] == delay_max + 1) && + (right_edge[i] != delay_max + 1)) { + right_edge[i] = delay_max + 1; + debug_cond(DLEVEL >= 2, + "%s:%d vfifo_center: reset right_edge[%u]: %d\n", + __func__, __LINE__, i, right_edge[i]); + } + + /* + * Reset sticky bit + * READ: except for bits where we have seen both + * the left and right edge. + * WRITE: except for bits where we have seen the + * left edge. + */ + *sticky_bit_chk <<= 1; + if (write) { + if (left_edge[i] != delay_max + 1) + *sticky_bit_chk |= 1; + } else { + if ((left_edge[i] != delay_max + 1) && + (right_edge[i] != delay_max + 1)) + *sticky_bit_chk |= 1; + } + } +} + +/** + * search_right_edge() - Find right edge of DQ/DQS working phase + * @write: Perform read (Stage 2) or write (Stage 3) calibration + * @rank_bgn: Rank number + * @write_group: Write Group + * @read_group: Read Group + * @start_dqs: DQS start phase + * @start_dqs_en: DQS enable start phase + * @sticky_bit_chk: Resulting sticky bit mask after the test + * @left_edge: Left edge of the DQ/DQS phase + * @right_edge: Right edge of the DQ/DQS phase + * @use_read_test: Perform read test + * + * Find right edge of DQ/DQS working phase. + */ +static int search_right_edge(struct socfpga_sdrseq *seq, const int write, + const int rank_bgn, const u32 write_group, + const u32 read_group, const int start_dqs, + const int start_dqs_en, u32 *sticky_bit_chk, + int *left_edge, int *right_edge, + const u32 use_read_test) +{ + const u32 delay_max = write ? seq->iocfg->io_out1_delay_max : + seq->iocfg->io_in_delay_max; + const u32 dqs_max = write ? seq->iocfg->io_out1_delay_max : + seq->iocfg->dqs_in_delay_max; + const u32 per_dqs = write ? seq->rwcfg->mem_dq_per_write_dqs : + seq->rwcfg->mem_dq_per_read_dqs; + u32 stop, bit_chk; + int i, d; + + for (d = 0; d <= dqs_max - start_dqs; d++) { + if (write) { /* WRITE-ONLY */ + scc_mgr_apply_group_dqs_io_and_oct_out1(seq, + write_group, + d + start_dqs); + } else { /* READ-ONLY */ + scc_mgr_set_dqs_bus_in_delay(read_group, d + start_dqs); + if (seq->iocfg->shift_dqs_en_when_shift_dqs) { + u32 delay = d + start_dqs_en; + if (delay > seq->iocfg->dqs_en_delay_max) + delay = seq->iocfg->dqs_en_delay_max; + scc_mgr_set_dqs_en_delay(read_group, delay); + } + scc_mgr_load_dqs(read_group); + } + + writel(0, &sdr_scc_mgr->update); + + stop = search_stop_check(seq, write, d, rank_bgn, write_group, + read_group, &bit_chk, sticky_bit_chk, + use_read_test); + if (stop == 1) { + if (write && (d == 0)) { /* WRITE-ONLY */ + for (i = 0; + i < seq->rwcfg->mem_dq_per_write_dqs; + i++) { + /* + * d = 0 failed, but it passed when + * testing the left edge, so it must be + * marginal, set it to -1 + */ + if (right_edge[i] == delay_max + 1 && + left_edge[i] != delay_max + 1) + right_edge[i] = -1; + } + } + break; + } + + /* stop != 1 */ + for (i = 0; i < per_dqs; i++) { + if (bit_chk & 1) { + /* + * Remember a passing test as + * the right_edge. + */ + right_edge[i] = d; + } else { + if (d != 0) { + /* + * If a right edge has not + * been seen yet, then a future + * passing test will mark this + * edge as the left edge. + */ + if (right_edge[i] == delay_max + 1) + left_edge[i] = -(d + 1); + } else { + /* + * d = 0 failed, but it passed + * when testing the left edge, + * so it must be marginal, set + * it to -1 + */ + if (right_edge[i] == delay_max + 1 && + left_edge[i] != delay_max + 1) + right_edge[i] = -1; + /* + * If a right edge has not been + * seen yet, then a future + * passing test will mark this + * edge as the left edge. + */ + else if (right_edge[i] == delay_max + 1) + left_edge[i] = -(d + 1); + } + } + + debug_cond(DLEVEL >= 2, "%s:%d center[r,d=%u]: ", + __func__, __LINE__, d); + debug_cond(DLEVEL >= 2, + "bit_chk_test=%i left_edge[%u]: %d ", + bit_chk & 1, i, left_edge[i]); + debug_cond(DLEVEL >= 2, "right_edge[%u]: %d\n", i, + right_edge[i]); + bit_chk >>= 1; + } + } + + /* Check that all bits have a window */ + for (i = 0; i < per_dqs; i++) { + debug_cond(DLEVEL >= 2, + "%s:%d write_center: left_edge[%u]: %d right_edge[%u]: %d", + __func__, __LINE__, i, left_edge[i], + i, right_edge[i]); + if ((left_edge[i] == dqs_max + 1) || + (right_edge[i] == dqs_max + 1)) + return i + 1; /* FIXME: If we fail, retval > 0 */ + } + + return 0; +} + +/** + * get_window_mid_index() - Find the best middle setting of DQ/DQS phase + * @write: Perform read (Stage 2) or write (Stage 3) calibration + * @left_edge: Left edge of the DQ/DQS phase + * @right_edge: Right edge of the DQ/DQS phase + * @mid_min: Best DQ/DQS phase middle setting + * + * Find index and value of the middle of the DQ/DQS working phase. + */ +static int get_window_mid_index(struct socfpga_sdrseq *seq, + const int write, int *left_edge, + int *right_edge, int *mid_min) +{ + const u32 per_dqs = write ? seq->rwcfg->mem_dq_per_write_dqs : + seq->rwcfg->mem_dq_per_read_dqs; + int i, mid, min_index; + + /* Find middle of window for each DQ bit */ + *mid_min = left_edge[0] - right_edge[0]; + min_index = 0; + for (i = 1; i < per_dqs; i++) { + mid = left_edge[i] - right_edge[i]; + if (mid < *mid_min) { + *mid_min = mid; + min_index = i; + } + } + + /* + * -mid_min/2 represents the amount that we need to move DQS. + * If mid_min is odd and positive we'll need to add one to make + * sure the rounding in further calculations is correct (always + * bias to the right), so just add 1 for all positive values. + */ + if (*mid_min > 0) + (*mid_min)++; + *mid_min = *mid_min / 2; + + debug_cond(DLEVEL >= 1, "%s:%d vfifo_center: *mid_min=%d (index=%u)\n", + __func__, __LINE__, *mid_min, min_index); + return min_index; +} + +/** + * center_dq_windows() - Center the DQ/DQS windows + * @write: Perform read (Stage 2) or write (Stage 3) calibration + * @left_edge: Left edge of the DQ/DQS phase + * @right_edge: Right edge of the DQ/DQS phase + * @mid_min: Adjusted DQ/DQS phase middle setting + * @orig_mid_min: Original DQ/DQS phase middle setting + * @min_index: DQ/DQS phase middle setting index + * @test_bgn: Rank number to begin the test + * @dq_margin: Amount of shift for the DQ + * @dqs_margin: Amount of shift for the DQS + * + * Align the DQ/DQS windows in each group. + */ +static void center_dq_windows(struct socfpga_sdrseq *seq, + const int write, int *left_edge, int *right_edge, + const int mid_min, const int orig_mid_min, + const int min_index, const int test_bgn, + int *dq_margin, int *dqs_margin) +{ + const s32 delay_max = write ? seq->iocfg->io_out1_delay_max : + seq->iocfg->io_in_delay_max; + const s32 per_dqs = write ? seq->rwcfg->mem_dq_per_write_dqs : + seq->rwcfg->mem_dq_per_read_dqs; + const s32 delay_off = write ? SCC_MGR_IO_OUT1_DELAY_OFFSET : + SCC_MGR_IO_IN_DELAY_OFFSET; + const s32 addr = SDR_PHYGRP_SCCGRP_ADDRESS | delay_off; + + s32 temp_dq_io_delay1; + int shift_dq, i, p; + + /* Initialize data for export structures */ + *dqs_margin = delay_max + 1; + *dq_margin = delay_max + 1; + + /* add delay to bring centre of all DQ windows to the same "level" */ + for (i = 0, p = test_bgn; i < per_dqs; i++, p++) { + /* Use values before divide by 2 to reduce round off error */ + shift_dq = (left_edge[i] - right_edge[i] - + (left_edge[min_index] - right_edge[min_index]))/2 + + (orig_mid_min - mid_min); + + debug_cond(DLEVEL >= 2, + "vfifo_center: before: shift_dq[%u]=%d\n", + i, shift_dq); + + temp_dq_io_delay1 = readl(addr + (i << 2)); + + if (shift_dq + temp_dq_io_delay1 > delay_max) + shift_dq = delay_max - temp_dq_io_delay1; + else if (shift_dq + temp_dq_io_delay1 < 0) + shift_dq = -temp_dq_io_delay1; + + debug_cond(DLEVEL >= 2, + "vfifo_center: after: shift_dq[%u]=%d\n", + i, shift_dq); + + if (write) + scc_mgr_set_dq_out1_delay(i, + temp_dq_io_delay1 + shift_dq); + else + scc_mgr_set_dq_in_delay(p, + temp_dq_io_delay1 + shift_dq); + + scc_mgr_load_dq(p); + + debug_cond(DLEVEL >= 2, + "vfifo_center: margin[%u]=[%d,%d]\n", i, + left_edge[i] - shift_dq + (-mid_min), + right_edge[i] + shift_dq - (-mid_min)); + + /* To determine values for export structures */ + if (left_edge[i] - shift_dq + (-mid_min) < *dq_margin) + *dq_margin = left_edge[i] - shift_dq + (-mid_min); + + if (right_edge[i] + shift_dq - (-mid_min) < *dqs_margin) + *dqs_margin = right_edge[i] + shift_dq - (-mid_min); + } +} + +/** + * rw_mgr_mem_calibrate_vfifo_center() - Per-bit deskew DQ and centering + * @rank_bgn: Rank number + * @rw_group: Read/Write Group + * @test_bgn: Rank at which the test begins + * @use_read_test: Perform a read test + * @update_fom: Update FOM + * + * Per-bit deskew DQ and centering. + */ +static int rw_mgr_mem_calibrate_vfifo_center(struct socfpga_sdrseq *seq, + const u32 rank_bgn, + const u32 rw_group, + const u32 test_bgn, + const int use_read_test, + const int update_fom) +{ + const u32 addr = + SDR_PHYGRP_SCCGRP_ADDRESS + SCC_MGR_DQS_IN_DELAY_OFFSET + + (rw_group << 2); + /* + * Store these as signed since there are comparisons with + * signed numbers. + */ + u32 sticky_bit_chk; + s32 left_edge[seq->rwcfg->mem_dq_per_read_dqs]; + s32 right_edge[seq->rwcfg->mem_dq_per_read_dqs]; + s32 orig_mid_min, mid_min; + s32 new_dqs, start_dqs, start_dqs_en = 0, final_dqs_en; + s32 dq_margin, dqs_margin; + int i, min_index; + int ret; + + debug("%s:%d: %u %u", __func__, __LINE__, rw_group, test_bgn); + + start_dqs = readl(addr); + if (seq->iocfg->shift_dqs_en_when_shift_dqs) + start_dqs_en = readl(addr - seq->iocfg->dqs_en_delay_offset); + + /* set the left and right edge of each bit to an illegal value */ + /* use (seq->iocfg->io_in_delay_max + 1) as an illegal value */ + sticky_bit_chk = 0; + for (i = 0; i < seq->rwcfg->mem_dq_per_read_dqs; i++) { + left_edge[i] = seq->iocfg->io_in_delay_max + 1; + right_edge[i] = seq->iocfg->io_in_delay_max + 1; + } + + /* Search for the left edge of the window for each bit */ + search_left_edge(seq, 0, rank_bgn, rw_group, rw_group, test_bgn, + &sticky_bit_chk, + left_edge, right_edge, use_read_test); + + + /* Search for the right edge of the window for each bit */ + ret = search_right_edge(seq, 0, rank_bgn, rw_group, rw_group, + start_dqs, start_dqs_en, + &sticky_bit_chk, + left_edge, right_edge, use_read_test); + if (ret) { + /* + * Restore delay chain settings before letting the loop + * in rw_mgr_mem_calibrate_vfifo to retry different + * dqs/ck relationships. + */ + scc_mgr_set_dqs_bus_in_delay(rw_group, start_dqs); + if (seq->iocfg->shift_dqs_en_when_shift_dqs) + scc_mgr_set_dqs_en_delay(rw_group, start_dqs_en); + + scc_mgr_load_dqs(rw_group); + writel(0, &sdr_scc_mgr->update); + + debug_cond(DLEVEL >= 1, + "%s:%d vfifo_center: failed to find edge [%u]: %d %d", + __func__, __LINE__, i, left_edge[i], right_edge[i]); + if (use_read_test) { + set_failing_group_stage(seq, rw_group * + seq->rwcfg->mem_dq_per_read_dqs + i, + CAL_STAGE_VFIFO, + CAL_SUBSTAGE_VFIFO_CENTER); + } else { + set_failing_group_stage(seq, rw_group * + seq->rwcfg->mem_dq_per_read_dqs + i, + CAL_STAGE_VFIFO_AFTER_WRITES, + CAL_SUBSTAGE_VFIFO_CENTER); + } + return -EIO; + } + + min_index = get_window_mid_index(seq, 0, left_edge, right_edge, + &mid_min); + + /* Determine the amount we can change DQS (which is -mid_min) */ + orig_mid_min = mid_min; + new_dqs = start_dqs - mid_min; + if (new_dqs > seq->iocfg->dqs_in_delay_max) + new_dqs = seq->iocfg->dqs_in_delay_max; + else if (new_dqs < 0) + new_dqs = 0; + + mid_min = start_dqs - new_dqs; + debug_cond(DLEVEL >= 1, "vfifo_center: new mid_min=%d new_dqs=%d\n", + mid_min, new_dqs); + + if (seq->iocfg->shift_dqs_en_when_shift_dqs) { + if (start_dqs_en - mid_min > seq->iocfg->dqs_en_delay_max) + mid_min += start_dqs_en - mid_min - + seq->iocfg->dqs_en_delay_max; + else if (start_dqs_en - mid_min < 0) + mid_min += start_dqs_en - mid_min; + } + new_dqs = start_dqs - mid_min; + + debug_cond(DLEVEL >= 1, + "vfifo_center: start_dqs=%d start_dqs_en=%d new_dqs=%d mid_min=%d\n", + start_dqs, + seq->iocfg->shift_dqs_en_when_shift_dqs ? start_dqs_en : -1, + new_dqs, mid_min); + + /* Add delay to bring centre of all DQ windows to the same "level". */ + center_dq_windows(seq, 0, left_edge, right_edge, mid_min, orig_mid_min, + min_index, test_bgn, &dq_margin, &dqs_margin); + + /* Move DQS-en */ + if (seq->iocfg->shift_dqs_en_when_shift_dqs) { + final_dqs_en = start_dqs_en - mid_min; + scc_mgr_set_dqs_en_delay(rw_group, final_dqs_en); + scc_mgr_load_dqs(rw_group); + } + + /* Move DQS */ + scc_mgr_set_dqs_bus_in_delay(rw_group, new_dqs); + scc_mgr_load_dqs(rw_group); + debug_cond(DLEVEL >= 2, + "%s:%d vfifo_center: dq_margin=%d dqs_margin=%d", + __func__, __LINE__, dq_margin, dqs_margin); + + /* + * Do not remove this line as it makes sure all of our decisions + * have been applied. Apply the update bit. + */ + writel(0, &sdr_scc_mgr->update); + + if ((dq_margin < 0) || (dqs_margin < 0)) + return -EINVAL; + + return 0; +} + +/** + * rw_mgr_mem_calibrate_guaranteed_write() - Perform guaranteed write into the + * device + * @rw_group: Read/Write Group + * @phase: DQ/DQS phase + * + * Because initially no communication ca be reliably performed with the memory + * device, the sequencer uses a guaranteed write mechanism to write data into + * the memory device. + */ +static int rw_mgr_mem_calibrate_guaranteed_write(struct socfpga_sdrseq *seq, + const u32 rw_group, + const u32 phase) +{ + int ret; + + /* Set a particular DQ/DQS phase. */ + scc_mgr_set_dqdqs_output_phase_all_ranks(seq, rw_group, phase); + + debug_cond(DLEVEL >= 1, "%s:%d guaranteed write: g=%u p=%u\n", + __func__, __LINE__, rw_group, phase); + + /* + * Altera EMI_RM 2015.05.04 :: Figure 1-25 + * Load up the patterns used by read calibration using the + * current DQDQS phase. + */ + rw_mgr_mem_calibrate_read_load_patterns(seq, 0, 1); + + if (seq->gbl.phy_debug_mode_flags & PHY_DEBUG_DISABLE_GUARANTEED_READ) + return 0; + + /* + * Altera EMI_RM 2015.05.04 :: Figure 1-26 + * Back-to-Back reads of the patterns used for calibration. + */ + ret = rw_mgr_mem_calibrate_read_test_patterns(seq, 0, rw_group, 1); + if (ret) + debug_cond(DLEVEL >= 1, + "%s:%d Guaranteed read test failed: g=%u p=%u\n", + __func__, __LINE__, rw_group, phase); + return ret; +} + +/** + * rw_mgr_mem_calibrate_dqs_enable_calibration() - DQS Enable Calibration + * @rw_group: Read/Write Group + * @test_bgn: Rank at which the test begins + * + * DQS enable calibration ensures reliable capture of the DQ signal without + * glitches on the DQS line. + */ +static int +rw_mgr_mem_calibrate_dqs_enable_calibration(struct socfpga_sdrseq *seq, + const u32 rw_group, + const u32 test_bgn) +{ + /* + * Altera EMI_RM 2015.05.04 :: Figure 1-27 + * DQS and DQS Eanble Signal Relationships. + */ + + /* We start at zero, so have one less dq to devide among */ + const u32 delay_step = seq->iocfg->io_in_delay_max / + (seq->rwcfg->mem_dq_per_read_dqs - 1); + int ret; + u32 i, p, d, r; + + debug("%s:%d (%u,%u)\n", __func__, __LINE__, rw_group, test_bgn); + + /* Try different dq_in_delays since the DQ path is shorter than DQS. */ + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + for (i = 0, p = test_bgn, d = 0; + i < seq->rwcfg->mem_dq_per_read_dqs; + i++, p++, d += delay_step) { + debug_cond(DLEVEL >= 1, + "%s:%d: g=%u r=%u i=%u p=%u d=%u\n", + __func__, __LINE__, rw_group, r, i, p, d); + + scc_mgr_set_dq_in_delay(p, d); + scc_mgr_load_dq(p); + } + + writel(0, &sdr_scc_mgr->update); + } + + /* + * Try rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase across different + * dq_in_delay values + */ + ret = rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase(seq, rw_group); + + debug_cond(DLEVEL >= 1, + "%s:%d: g=%u found=%u; Reseting delay chain to zero\n", + __func__, __LINE__, rw_group, !ret); + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + scc_mgr_apply_group_dq_in_delay(seq, test_bgn, 0); + writel(0, &sdr_scc_mgr->update); + } + + return ret; +} + +/** + * rw_mgr_mem_calibrate_dq_dqs_centering() - Centering DQ/DQS + * @rw_group: Read/Write Group + * @test_bgn: Rank at which the test begins + * @use_read_test: Perform a read test + * @update_fom: Update FOM + * + * The centerin DQ/DQS stage attempts to align DQ and DQS signals on reads + * within a group. + */ +static int +rw_mgr_mem_calibrate_dq_dqs_centering(struct socfpga_sdrseq *seq, + const u32 rw_group, const u32 test_bgn, + const int use_read_test, + const int update_fom) + +{ + int ret, grp_calibrated; + u32 rank_bgn, sr; + + /* + * Altera EMI_RM 2015.05.04 :: Figure 1-28 + * Read per-bit deskew can be done on a per shadow register basis. + */ + grp_calibrated = 1; + for (rank_bgn = 0, sr = 0; + rank_bgn < seq->rwcfg->mem_number_of_ranks; + rank_bgn += NUM_RANKS_PER_SHADOW_REG, sr++) { + ret = rw_mgr_mem_calibrate_vfifo_center(seq, rank_bgn, rw_group, + test_bgn, + use_read_test, + update_fom); + if (!ret) + continue; + + grp_calibrated = 0; + } + + if (!grp_calibrated) + return -EIO; + + return 0; +} + +/** + * rw_mgr_mem_calibrate_vfifo() - Calibrate the read valid prediction FIFO + * @rw_group: Read/Write Group + * @test_bgn: Rank at which the test begins + * + * Stage 1: Calibrate the read valid prediction FIFO. + * + * This function implements UniPHY calibration Stage 1, as explained in + * detail in Altera EMI_RM 2015.05.04 , "UniPHY Calibration Stages". + * + * - read valid prediction will consist of finding: + * - DQS enable phase and DQS enable delay (DQS Enable Calibration) + * - DQS input phase and DQS input delay (DQ/DQS Centering) + * - we also do a per-bit deskew on the DQ lines. + */ +static int rw_mgr_mem_calibrate_vfifo(struct socfpga_sdrseq *seq, + const u32 rw_group, const u32 test_bgn) +{ + u32 p, d; + u32 dtaps_per_ptap; + u32 failed_substage; + + int ret; + + debug("%s:%d: %u %u\n", __func__, __LINE__, rw_group, test_bgn); + + /* Update info for sims */ + reg_file_set_group(rw_group); + reg_file_set_stage(CAL_STAGE_VFIFO); + reg_file_set_sub_stage(CAL_SUBSTAGE_GUARANTEED_READ); + + failed_substage = CAL_SUBSTAGE_GUARANTEED_READ; + + /* USER Determine number of delay taps for each phase tap. */ + dtaps_per_ptap = DIV_ROUND_UP(seq->iocfg->delay_per_opa_tap, + seq->iocfg->delay_per_dqs_en_dchain_tap) + - 1; + + for (d = 0; d <= dtaps_per_ptap; d += 2) { + /* + * In RLDRAMX we may be messing the delay of pins in + * the same write rw_group but outside of the current read + * the rw_group, but that's ok because we haven't calibrated + * output side yet. + */ + if (d > 0) { + scc_mgr_apply_group_all_out_delay_add_all_ranks(seq, + rw_group, + d); + } + + for (p = 0; p <= seq->iocfg->dqdqs_out_phase_max; p++) { + /* 1) Guaranteed Write */ + ret = rw_mgr_mem_calibrate_guaranteed_write(seq, + rw_group, + p); + if (ret) + break; + + /* 2) DQS Enable Calibration */ + ret = rw_mgr_mem_calibrate_dqs_enable_calibration(seq, + rw_group, + test_bgn); + if (ret) { + failed_substage = CAL_SUBSTAGE_DQS_EN_PHASE; + continue; + } + + /* 3) Centering DQ/DQS */ + /* + * If doing read after write calibration, do not update + * FOM now. Do it then. + */ + ret = rw_mgr_mem_calibrate_dq_dqs_centering(seq, + rw_group, + test_bgn, + 1, 0); + if (ret) { + failed_substage = CAL_SUBSTAGE_VFIFO_CENTER; + continue; + } + + /* All done. */ + goto cal_done_ok; + } + } + + /* Calibration Stage 1 failed. */ + set_failing_group_stage(seq, rw_group, CAL_STAGE_VFIFO, + failed_substage); + return 0; + + /* Calibration Stage 1 completed OK. */ +cal_done_ok: + /* + * Reset the delay chains back to zero if they have moved > 1 + * (check for > 1 because loop will increase d even when pass in + * first case). + */ + if (d > 2) + scc_mgr_zero_group(seq, rw_group, 1); + + return 1; +} + +/** + * rw_mgr_mem_calibrate_vfifo_end() - DQ/DQS Centering. + * @rw_group: Read/Write Group + * @test_bgn: Rank at which the test begins + * + * Stage 3: DQ/DQS Centering. + * + * This function implements UniPHY calibration Stage 3, as explained in + * detail in Altera EMI_RM 2015.05.04 , "UniPHY Calibration Stages". + */ +static int rw_mgr_mem_calibrate_vfifo_end(struct socfpga_sdrseq *seq, + const u32 rw_group, + const u32 test_bgn) +{ + int ret; + + debug("%s:%d %u %u", __func__, __LINE__, rw_group, test_bgn); + + /* Update info for sims. */ + reg_file_set_group(rw_group); + reg_file_set_stage(CAL_STAGE_VFIFO_AFTER_WRITES); + reg_file_set_sub_stage(CAL_SUBSTAGE_VFIFO_CENTER); + + ret = rw_mgr_mem_calibrate_dq_dqs_centering(seq, rw_group, test_bgn, 0, + 1); + if (ret) + set_failing_group_stage(seq, rw_group, + CAL_STAGE_VFIFO_AFTER_WRITES, + CAL_SUBSTAGE_VFIFO_CENTER); + return ret; +} + +/** + * rw_mgr_mem_calibrate_lfifo() - Minimize latency + * + * Stage 4: Minimize latency. + * + * This function implements UniPHY calibration Stage 4, as explained in + * detail in Altera EMI_RM 2015.05.04 , "UniPHY Calibration Stages". + * Calibrate LFIFO to find smallest read latency. + */ +static u32 rw_mgr_mem_calibrate_lfifo(struct socfpga_sdrseq *seq) +{ + int found_one = 0; + + debug("%s:%d\n", __func__, __LINE__); + + /* Update info for sims. */ + reg_file_set_stage(CAL_STAGE_LFIFO); + reg_file_set_sub_stage(CAL_SUBSTAGE_READ_LATENCY); + + /* Load up the patterns used by read calibration for all ranks */ + rw_mgr_mem_calibrate_read_load_patterns(seq, 0, 1); + + do { + writel(seq->gbl.curr_read_lat, &phy_mgr_cfg->phy_rlat); + debug_cond(DLEVEL >= 2, "%s:%d lfifo: read_lat=%u", + __func__, __LINE__, seq->gbl.curr_read_lat); + + if (!rw_mgr_mem_calibrate_read_test_all_ranks(seq, 0, + NUM_READ_TESTS, + PASS_ALL_BITS, 1)) + break; + + found_one = 1; + /* + * Reduce read latency and see if things are + * working correctly. + */ + seq->gbl.curr_read_lat--; + } while (seq->gbl.curr_read_lat > 0); + + /* Reset the fifos to get pointers to known state. */ + writel(0, &phy_mgr_cmd->fifo_reset); + + if (found_one) { + /* Add a fudge factor to the read latency that was determined */ + seq->gbl.curr_read_lat += 2; + writel(seq->gbl.curr_read_lat, &phy_mgr_cfg->phy_rlat); + debug_cond(DLEVEL >= 2, + "%s:%d lfifo: success: using read_lat=%u\n", + __func__, __LINE__, seq->gbl.curr_read_lat); + } else { + set_failing_group_stage(seq, 0xff, CAL_STAGE_LFIFO, + CAL_SUBSTAGE_READ_LATENCY); + + debug_cond(DLEVEL >= 2, + "%s:%d lfifo: failed at initial read_lat=%u\n", + __func__, __LINE__, seq->gbl.curr_read_lat); + } + + return found_one; +} + +/** + * search_window() - Search for the/part of the window with DM/DQS shift + * @search_dm: If 1, search for the DM shift, if 0, search for DQS + * shift + * @rank_bgn: Rank number + * @write_group: Write Group + * @bgn_curr: Current window begin + * @end_curr: Current window end + * @bgn_best: Current best window begin + * @end_best: Current best window end + * @win_best: Size of the best window + * @new_dqs: New DQS value (only applicable if search_dm = 0). + * + * Search for the/part of the window with DM/DQS shift. + */ +static void search_window(struct socfpga_sdrseq *seq, + const int search_dm, const u32 rank_bgn, + const u32 write_group, int *bgn_curr, int *end_curr, + int *bgn_best, int *end_best, int *win_best, + int new_dqs) +{ + u32 bit_chk; + const int max = seq->iocfg->io_out1_delay_max - new_dqs; + int d, di; + + /* Search for the/part of the window with DM/DQS shift. */ + for (di = max; di >= 0; di -= DELTA_D) { + if (search_dm) { + d = di; + scc_mgr_apply_group_dm_out1_delay(seq, d); + } else { + /* For DQS, we go from 0...max */ + d = max - di; + /* + * Note: This only shifts DQS, so are we limiting + * ourselves to width of DQ unnecessarily. + */ + scc_mgr_apply_group_dqs_io_and_oct_out1(seq, + write_group, + d + new_dqs); + } + + writel(0, &sdr_scc_mgr->update); + + if (rw_mgr_mem_calibrate_write_test(seq, rank_bgn, write_group, + 1, PASS_ALL_BITS, &bit_chk, + 0)) { + /* Set current end of the window. */ + *end_curr = search_dm ? -d : d; + + /* + * If a starting edge of our window has not been seen + * this is our current start of the DM window. + */ + if (*bgn_curr == seq->iocfg->io_out1_delay_max + 1) + *bgn_curr = search_dm ? -d : d; + + /* + * If current window is bigger than best seen. + * Set best seen to be current window. + */ + if ((*end_curr - *bgn_curr + 1) > *win_best) { + *win_best = *end_curr - *bgn_curr + 1; + *bgn_best = *bgn_curr; + *end_best = *end_curr; + } + } else { + /* We just saw a failing test. Reset temp edge. */ + *bgn_curr = seq->iocfg->io_out1_delay_max + 1; + *end_curr = seq->iocfg->io_out1_delay_max + 1; + + /* Early exit is only applicable to DQS. */ + if (search_dm) + continue; + + /* + * Early exit optimization: if the remaining delay + * chain space is less than already seen largest + * window we can exit. + */ + if (*win_best - 1 > seq->iocfg->io_out1_delay_max + - new_dqs - d) + break; + } + } +} + +/* + * rw_mgr_mem_calibrate_writes_center() - Center all windows + * @rank_bgn: Rank number + * @write_group: Write group + * @test_bgn: Rank at which the test begins + * + * Center all windows. Do per-bit-deskew to possibly increase size of + * certain windows. + */ +static int +rw_mgr_mem_calibrate_writes_center(struct socfpga_sdrseq *seq, + const u32 rank_bgn, const u32 write_group, + const u32 test_bgn) +{ + int i; + u32 sticky_bit_chk; + u32 min_index; + int left_edge[seq->rwcfg->mem_dq_per_write_dqs]; + int right_edge[seq->rwcfg->mem_dq_per_write_dqs]; + int mid; + int mid_min, orig_mid_min; + int new_dqs, start_dqs; + int dq_margin, dqs_margin, dm_margin; + int bgn_curr = seq->iocfg->io_out1_delay_max + 1; + int end_curr = seq->iocfg->io_out1_delay_max + 1; + int bgn_best = seq->iocfg->io_out1_delay_max + 1; + int end_best = seq->iocfg->io_out1_delay_max + 1; + int win_best = 0; + + int ret; + + debug("%s:%d %u %u", __func__, __LINE__, write_group, test_bgn); + + dm_margin = 0; + + start_dqs = readl((SDR_PHYGRP_SCCGRP_ADDRESS | + SCC_MGR_IO_OUT1_DELAY_OFFSET) + + (seq->rwcfg->mem_dq_per_write_dqs << 2)); + + /* Per-bit deskew. */ + + /* + * Set the left and right edge of each bit to an illegal value. + * Use (seq->iocfg->io_out1_delay_max + 1) as an illegal value. + */ + sticky_bit_chk = 0; + for (i = 0; i < seq->rwcfg->mem_dq_per_write_dqs; i++) { + left_edge[i] = seq->iocfg->io_out1_delay_max + 1; + right_edge[i] = seq->iocfg->io_out1_delay_max + 1; + } + + /* Search for the left edge of the window for each bit. */ + search_left_edge(seq, 1, rank_bgn, write_group, 0, test_bgn, + &sticky_bit_chk, + left_edge, right_edge, 0); + + /* Search for the right edge of the window for each bit. */ + ret = search_right_edge(seq, 1, rank_bgn, write_group, 0, + start_dqs, 0, + &sticky_bit_chk, + left_edge, right_edge, 0); + if (ret) { + set_failing_group_stage(seq, test_bgn + ret - 1, + CAL_STAGE_WRITES, + CAL_SUBSTAGE_WRITES_CENTER); + return -EINVAL; + } + + min_index = get_window_mid_index(seq, 1, left_edge, right_edge, + &mid_min); + + /* Determine the amount we can change DQS (which is -mid_min). */ + orig_mid_min = mid_min; + new_dqs = start_dqs; + mid_min = 0; + debug_cond(DLEVEL >= 1, + "%s:%d write_center: start_dqs=%d new_dqs=%d mid_min=%d\n", + __func__, __LINE__, start_dqs, new_dqs, mid_min); + + /* Add delay to bring centre of all DQ windows to the same "level". */ + center_dq_windows(seq, 1, left_edge, right_edge, mid_min, orig_mid_min, + min_index, 0, &dq_margin, &dqs_margin); + + /* Move DQS */ + scc_mgr_apply_group_dqs_io_and_oct_out1(seq, write_group, new_dqs); + writel(0, &sdr_scc_mgr->update); + + /* Centre DM */ + debug_cond(DLEVEL >= 2, "%s:%d write_center: DM\n", __func__, __LINE__); + + /* Search for the/part of the window with DM shift. */ + search_window(seq, 1, rank_bgn, write_group, &bgn_curr, &end_curr, + &bgn_best, &end_best, &win_best, 0); + + /* Reset DM delay chains to 0. */ + scc_mgr_apply_group_dm_out1_delay(seq, 0); + + /* + * Check to see if the current window nudges up aganist 0 delay. + * If so we need to continue the search by shifting DQS otherwise DQS + * search begins as a new search. + */ + if (end_curr != 0) { + bgn_curr = seq->iocfg->io_out1_delay_max + 1; + end_curr = seq->iocfg->io_out1_delay_max + 1; + } + + /* Search for the/part of the window with DQS shifts. */ + search_window(seq, 0, rank_bgn, write_group, &bgn_curr, &end_curr, + &bgn_best, &end_best, &win_best, new_dqs); + + /* Assign left and right edge for cal and reporting. */ + left_edge[0] = -1 * bgn_best; + right_edge[0] = end_best; + + debug_cond(DLEVEL >= 2, "%s:%d dm_calib: left=%d right=%d\n", + __func__, __LINE__, left_edge[0], right_edge[0]); + + /* Move DQS (back to orig). */ + scc_mgr_apply_group_dqs_io_and_oct_out1(seq, write_group, new_dqs); + + /* Move DM */ + + /* Find middle of window for the DM bit. */ + mid = (left_edge[0] - right_edge[0]) / 2; + + /* Only move right, since we are not moving DQS/DQ. */ + if (mid < 0) + mid = 0; + + /* dm_marign should fail if we never find a window. */ + if (win_best == 0) + dm_margin = -1; + else + dm_margin = left_edge[0] - mid; + + scc_mgr_apply_group_dm_out1_delay(seq, mid); + writel(0, &sdr_scc_mgr->update); + + debug_cond(DLEVEL >= 2, + "%s:%d dm_calib: left=%d right=%d mid=%d dm_margin=%d\n", + __func__, __LINE__, left_edge[0], right_edge[0], + mid, dm_margin); + /* Export values. */ + seq->gbl.fom_out += dq_margin + dqs_margin; + + debug_cond(DLEVEL >= 2, + "%s:%d write_center: dq_margin=%d dqs_margin=%d dm_margin=%d\n", + __func__, __LINE__, dq_margin, dqs_margin, dm_margin); + + /* + * Do not remove this line as it makes sure all of our + * decisions have been applied. + */ + writel(0, &sdr_scc_mgr->update); + + if ((dq_margin < 0) || (dqs_margin < 0) || (dm_margin < 0)) + return -EINVAL; + + return 0; +} + +/** + * rw_mgr_mem_calibrate_writes() - Write Calibration Part One + * @rank_bgn: Rank number + * @group: Read/Write Group + * @test_bgn: Rank at which the test begins + * + * Stage 2: Write Calibration Part One. + * + * This function implements UniPHY calibration Stage 2, as explained in + * detail in Altera EMI_RM 2015.05.04 , "UniPHY Calibration Stages". + */ +static int rw_mgr_mem_calibrate_writes(struct socfpga_sdrseq *seq, + const u32 rank_bgn, const u32 group, + const u32 test_bgn) +{ + int ret; + + /* Update info for sims */ + debug("%s:%d %u %u\n", __func__, __LINE__, group, test_bgn); + + reg_file_set_group(group); + reg_file_set_stage(CAL_STAGE_WRITES); + reg_file_set_sub_stage(CAL_SUBSTAGE_WRITES_CENTER); + + ret = rw_mgr_mem_calibrate_writes_center(seq, rank_bgn, group, + test_bgn); + if (ret) + set_failing_group_stage(seq, group, CAL_STAGE_WRITES, + CAL_SUBSTAGE_WRITES_CENTER); + + return ret; +} + +/** + * mem_precharge_and_activate() - Precharge all banks and activate + * + * Precharge all banks and activate row 0 in bank "000..." and bank "111...". + */ +static void mem_precharge_and_activate(struct socfpga_sdrseq *seq) +{ + int r; + + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; r++) { + /* Set rank. */ + set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_OFF); + + /* Precharge all banks. */ + writel(seq->rwcfg->precharge_all, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + + writel(0x0F, &sdr_rw_load_mgr_regs->load_cntr0); + writel(seq->rwcfg->activate_0_and_1_wait1, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(0x0F, &sdr_rw_load_mgr_regs->load_cntr1); + writel(seq->rwcfg->activate_0_and_1_wait2, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + /* Activate rows. */ + writel(seq->rwcfg->activate_0_and_1, + SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + } +} + +/** + * mem_init_latency() - Configure memory RLAT and WLAT settings + * + * Configure memory RLAT and WLAT parameters. + */ +static void mem_init_latency(struct socfpga_sdrseq *seq) +{ + /* + * For AV/CV, LFIFO is hardened and always runs at full rate + * so max latency in AFI clocks, used here, is correspondingly + * smaller. + */ + const u32 max_latency = (1 << seq->misccfg->max_latency_count_width) + - 1; + u32 rlat, wlat; + + debug("%s:%d\n", __func__, __LINE__); + + /* + * Read in write latency. + * WL for Hard PHY does not include additive latency. + */ + wlat = readl(&data_mgr->t_wl_add); + wlat += readl(&data_mgr->mem_t_add); + + seq->gbl.rw_wl_nop_cycles = wlat - 1; + + /* Read in readl latency. */ + rlat = readl(&data_mgr->t_rl_add); + + /* Set a pretty high read latency initially. */ + seq->gbl.curr_read_lat = rlat + 16; + if (seq->gbl.curr_read_lat > max_latency) + seq->gbl.curr_read_lat = max_latency; + + writel(seq->gbl.curr_read_lat, &phy_mgr_cfg->phy_rlat); + + /* Advertise write latency. */ + writel(wlat, &phy_mgr_cfg->afi_wlat); +} + +/** + * @mem_skip_calibrate() - Set VFIFO and LFIFO to instant-on settings + * + * Set VFIFO and LFIFO to instant-on settings in skip calibration mode. + */ +static void mem_skip_calibrate(struct socfpga_sdrseq *seq) +{ + u32 vfifo_offset; + u32 i, j, r; + + debug("%s:%d\n", __func__, __LINE__); + /* Need to update every shadow register set used by the interface */ + for (r = 0; r < seq->rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + /* + * Set output phase alignment settings appropriate for + * skip calibration. + */ + for (i = 0; i < seq->rwcfg->mem_if_read_dqs_width; i++) { + scc_mgr_set_dqs_en_phase(i, 0); + if (seq->iocfg->dll_chain_length == 6) + scc_mgr_set_dqdqs_output_phase(i, 6); + else + scc_mgr_set_dqdqs_output_phase(i, 7); + /* + * Case:33398 + * + * Write data arrives to the I/O two cycles before write + * latency is reached (720 deg). + * -> due to bit-slip in a/c bus + * -> to allow board skew where dqs is longer than ck + * -> how often can this happen!? + * -> can claim back some ptaps for high freq + * support if we can relax this, but i digress... + * + * The write_clk leads mem_ck by 90 deg + * The minimum ptap of the OPA is 180 deg + * Each ptap has (360 / IO_DLL_CHAIN_LENGH) deg of delay + * The write_clk is always delayed by 2 ptaps + * + * Hence, to make DQS aligned to CK, we need to delay + * DQS by: + * (720 - 90 - 180 - 2) * + * (360 / seq->iocfg->dll_chain_length) + * + * Dividing the above by + (360 / seq->iocfg->dll_chain_length) + * gives us the number of ptaps, which simplies to: + * + * (1.25 * seq->iocfg->dll_chain_length - 2) + */ + scc_mgr_set_dqdqs_output_phase(i, + ((125 * seq->iocfg->dll_chain_length) + / 100) - 2); + } + writel(0xff, &sdr_scc_mgr->dqs_ena); + writel(0xff, &sdr_scc_mgr->dqs_io_ena); + + for (i = 0; i < seq->rwcfg->mem_if_write_dqs_width; i++) { + writel(i, SDR_PHYGRP_SCCGRP_ADDRESS | + SCC_MGR_GROUP_COUNTER_OFFSET); + } + writel(0xff, &sdr_scc_mgr->dq_ena); + writel(0xff, &sdr_scc_mgr->dm_ena); + writel(0, &sdr_scc_mgr->update); + } + + /* Compensate for simulation model behaviour */ + for (i = 0; i < seq->rwcfg->mem_if_read_dqs_width; i++) { + scc_mgr_set_dqs_bus_in_delay(i, 10); + scc_mgr_load_dqs(i); + } + writel(0, &sdr_scc_mgr->update); + + /* + * ArriaV has hard FIFOs that can only be initialized by incrementing + * in sequencer. + */ + vfifo_offset = seq->misccfg->calib_vfifo_offset; + for (j = 0; j < vfifo_offset; j++) + writel(0xff, &phy_mgr_cmd->inc_vfifo_hard_phy); + writel(0, &phy_mgr_cmd->fifo_reset); + + /* + * For Arria V and Cyclone V with hard LFIFO, we get the skip-cal + * setting from generation-time constant. + */ + seq->gbl.curr_read_lat = seq->misccfg->calib_lfifo_offset; + writel(seq->gbl.curr_read_lat, &phy_mgr_cfg->phy_rlat); +} + +/** + * mem_calibrate() - Memory calibration entry point. + * + * Perform memory calibration. + */ +static u32 mem_calibrate(struct socfpga_sdrseq *seq) +{ + u32 i; + u32 rank_bgn, sr; + u32 write_group, write_test_bgn; + u32 read_group, read_test_bgn; + u32 run_groups, current_run; + u32 failing_groups = 0; + u32 group_failed = 0; + + const u32 rwdqs_ratio = seq->rwcfg->mem_if_read_dqs_width / + seq->rwcfg->mem_if_write_dqs_width; + + debug("%s:%d\n", __func__, __LINE__); + + /* Initialize the data settings */ + seq->gbl.error_substage = CAL_SUBSTAGE_NIL; + seq->gbl.error_stage = CAL_STAGE_NIL; + seq->gbl.error_group = 0xff; + seq->gbl.fom_in = 0; + seq->gbl.fom_out = 0; + + /* Initialize WLAT and RLAT. */ + mem_init_latency(seq); + + /* Initialize bit slips. */ + mem_precharge_and_activate(seq); + + for (i = 0; i < seq->rwcfg->mem_if_read_dqs_width; i++) { + writel(i, SDR_PHYGRP_SCCGRP_ADDRESS | + SCC_MGR_GROUP_COUNTER_OFFSET); + /* Only needed once to set all groups, pins, DQ, DQS, DM. */ + if (i == 0) + scc_mgr_set_hhp_extras(); + + scc_set_bypass_mode(i); + } + + /* Calibration is skipped. */ + if ((seq->dyn_calib_steps & CALIB_SKIP_ALL) == CALIB_SKIP_ALL) { + /* + * Set VFIFO and LFIFO to instant-on settings in skip + * calibration mode. + */ + mem_skip_calibrate(seq); + + /* + * Do not remove this line as it makes sure all of our + * decisions have been applied. + */ + writel(0, &sdr_scc_mgr->update); + return 1; + } + + /* Calibration is not skipped. */ + for (i = 0; i < NUM_CALIB_REPEAT; i++) { + /* + * Zero all delay chain/phase settings for all + * groups and all shadow register sets. + */ + scc_mgr_zero_all(seq); + + run_groups = ~0; + + for (write_group = 0, write_test_bgn = 0; write_group + < seq->rwcfg->mem_if_write_dqs_width; write_group++, + write_test_bgn += seq->rwcfg->mem_dq_per_write_dqs) { + /* Initialize the group failure */ + group_failed = 0; + + current_run = run_groups & ((1 << + RW_MGR_NUM_DQS_PER_WRITE_GROUP) - 1); + run_groups = run_groups >> + RW_MGR_NUM_DQS_PER_WRITE_GROUP; + + if (current_run == 0) + continue; + + writel(write_group, SDR_PHYGRP_SCCGRP_ADDRESS | + SCC_MGR_GROUP_COUNTER_OFFSET); + scc_mgr_zero_group(seq, write_group, 0); + + for (read_group = write_group * rwdqs_ratio, + read_test_bgn = 0; + read_group < (write_group + 1) * rwdqs_ratio; + read_group++, + read_test_bgn += seq->rwcfg->mem_dq_per_read_dqs) { + if (STATIC_CALIB_STEPS & CALIB_SKIP_VFIFO) + continue; + + /* Calibrate the VFIFO */ + if (rw_mgr_mem_calibrate_vfifo(seq, read_group, + read_test_bgn)) + continue; + + if (!(seq->gbl.phy_debug_mode_flags & + PHY_DEBUG_SWEEP_ALL_GROUPS)) + return 0; + + /* The group failed, we're done. */ + goto grp_failed; + } + + /* Calibrate the output side */ + for (rank_bgn = 0, sr = 0; + rank_bgn < seq->rwcfg->mem_number_of_ranks; + rank_bgn += NUM_RANKS_PER_SHADOW_REG, sr++) { + if (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES) + continue; + + /* Not needed in quick mode! */ + if (STATIC_CALIB_STEPS & + CALIB_SKIP_DELAY_SWEEPS) + continue; + + /* Calibrate WRITEs */ + if (!rw_mgr_mem_calibrate_writes(seq, rank_bgn, + write_group, + write_test_bgn)) + continue; + + group_failed = 1; + if (!(seq->gbl.phy_debug_mode_flags & + PHY_DEBUG_SWEEP_ALL_GROUPS)) + return 0; + } + + /* Some group failed, we're done. */ + if (group_failed) + goto grp_failed; + + for (read_group = write_group * rwdqs_ratio, + read_test_bgn = 0; + read_group < (write_group + 1) * rwdqs_ratio; + read_group++, + read_test_bgn += seq->rwcfg->mem_dq_per_read_dqs) { + if (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES) + continue; + + if (!rw_mgr_mem_calibrate_vfifo_end(seq, + read_group, + read_test_bgn)) + continue; + + if (!(seq->gbl.phy_debug_mode_flags & + PHY_DEBUG_SWEEP_ALL_GROUPS)) + return 0; + + /* The group failed, we're done. */ + goto grp_failed; + } + + /* No group failed, continue as usual. */ + continue; + +grp_failed: /* A group failed, increment the counter. */ + failing_groups++; + } + + /* + * USER If there are any failing groups then report + * the failure. + */ + if (failing_groups != 0) + return 0; + + if (STATIC_CALIB_STEPS & CALIB_SKIP_LFIFO) + continue; + + /* Calibrate the LFIFO */ + if (!rw_mgr_mem_calibrate_lfifo(seq)) + return 0; + } + + /* + * Do not remove this line as it makes sure all of our decisions + * have been applied. + */ + writel(0, &sdr_scc_mgr->update); + return 1; +} + +/** + * run_mem_calibrate() - Perform memory calibration + * + * This function triggers the entire memory calibration procedure. + */ +static int run_mem_calibrate(struct socfpga_sdrseq *seq) +{ + int pass; + u32 ctrl_cfg; + + debug("%s:%d\n", __func__, __LINE__); + + /* Reset pass/fail status shown on afi_cal_success/fail */ + writel(PHY_MGR_CAL_RESET, &phy_mgr_cfg->cal_status); + + /* Stop tracking manager. */ + ctrl_cfg = readl(&sdr_ctrl->ctrl_cfg); + writel(ctrl_cfg & ~SDR_CTRLGRP_CTRLCFG_DQSTRKEN_MASK, + &sdr_ctrl->ctrl_cfg); + + phy_mgr_initialize(seq); + rw_mgr_mem_initialize(seq); + + /* Perform the actual memory calibration. */ + pass = mem_calibrate(seq); + + mem_precharge_and_activate(seq); + writel(0, &phy_mgr_cmd->fifo_reset); + + /* Handoff. */ + rw_mgr_mem_handoff(seq); + /* + * In Hard PHY this is a 2-bit control: + * 0: AFI Mux Select + * 1: DDIO Mux Select + */ + writel(0x2, &phy_mgr_cfg->mux_sel); + + /* Start tracking manager. */ + writel(ctrl_cfg, &sdr_ctrl->ctrl_cfg); + + return pass; +} + +/** + * debug_mem_calibrate() - Report result of memory calibration + * @pass: Value indicating whether calibration passed or failed + * + * This function reports the results of the memory calibration + * and writes debug information into the register file. + */ +static void debug_mem_calibrate(struct socfpga_sdrseq *seq, int pass) +{ + u32 debug_info; + + if (pass) { + debug("%s: CALIBRATION PASSED\n", __FILE__); + + seq->gbl.fom_in /= 2; + seq->gbl.fom_out /= 2; + + if (seq->gbl.fom_in > 0xff) + seq->gbl.fom_in = 0xff; + + if (seq->gbl.fom_out > 0xff) + seq->gbl.fom_out = 0xff; + + /* Update the FOM in the register file */ + debug_info = seq->gbl.fom_in; + debug_info |= seq->gbl.fom_out << 8; + writel(debug_info, &sdr_reg_file->fom); + + writel(debug_info, &phy_mgr_cfg->cal_debug_info); + writel(PHY_MGR_CAL_SUCCESS, &phy_mgr_cfg->cal_status); + } else { + debug("%s: CALIBRATION FAILED\n", __FILE__); + + debug_info = seq->gbl.error_stage; + debug_info |= seq->gbl.error_substage << 8; + debug_info |= seq->gbl.error_group << 16; + + writel(debug_info, &sdr_reg_file->failing_stage); + writel(debug_info, &phy_mgr_cfg->cal_debug_info); + writel(PHY_MGR_CAL_FAIL, &phy_mgr_cfg->cal_status); + + /* Update the failing group/stage in the register file */ + debug_info = seq->gbl.error_stage; + debug_info |= seq->gbl.error_substage << 8; + debug_info |= seq->gbl.error_group << 16; + writel(debug_info, &sdr_reg_file->failing_stage); + } + + debug("%s: Calibration complete\n", __FILE__); +} + +/** + * hc_initialize_rom_data() - Initialize ROM data + * + * Initialize ROM data. + */ +static void hc_initialize_rom_data(void) +{ + unsigned int nelem = 0; + const u32 *rom_init; + u32 i, addr; + + socfpga_get_seq_inst_init(&rom_init, &nelem); + addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_INST_ROM_WRITE_OFFSET; + for (i = 0; i < nelem; i++) + writel(rom_init[i], addr + (i << 2)); + + socfpga_get_seq_ac_init(&rom_init, &nelem); + addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_AC_ROM_WRITE_OFFSET; + for (i = 0; i < nelem; i++) + writel(rom_init[i], addr + (i << 2)); +} + +/** + * initialize_reg_file() - Initialize SDR register file + * + * Initialize SDR register file. + */ +static void initialize_reg_file(struct socfpga_sdrseq *seq) +{ + /* Initialize the register file with the correct data */ + writel(seq->misccfg->reg_file_init_seq_signature, + &sdr_reg_file->signature); + writel(0, &sdr_reg_file->debug_data_addr); + writel(0, &sdr_reg_file->cur_stage); + writel(0, &sdr_reg_file->fom); + writel(0, &sdr_reg_file->failing_stage); + writel(0, &sdr_reg_file->debug1); + writel(0, &sdr_reg_file->debug2); +} + +/** + * initialize_hps_phy() - Initialize HPS PHY + * + * Initialize HPS PHY. + */ +static void initialize_hps_phy(void) +{ + u32 reg; + /* + * Tracking also gets configured here because it's in the + * same register. + */ + u32 trk_sample_count = 7500; + u32 trk_long_idle_sample_count = (10 << 16) | 100; + /* + * Format is number of outer loops in the 16 MSB, sample + * count in 16 LSB. + */ + + reg = 0; + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_ACDELAYEN_SET(2); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQDELAYEN_SET(1); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQSDELAYEN_SET(1); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQSLOGICDELAYEN_SET(1); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_RESETDELAYEN_SET(0); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_LPDDRDIS_SET(1); + /* + * This field selects the intrinsic latency to RDATA_EN/FULL path. + * 00-bypass, 01- add 5 cycles, 10- add 10 cycles, 11- add 15 cycles. + */ + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_ADDLATSEL_SET(0); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_SET( + trk_sample_count); + writel(reg, &sdr_ctrl->phy_ctrl0); + + reg = 0; + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_SAMPLECOUNT_31_20_SET( + trk_sample_count >> + SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_WIDTH); + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_LONGIDLESAMPLECOUNT_19_0_SET( + trk_long_idle_sample_count); + writel(reg, &sdr_ctrl->phy_ctrl1); + + reg = 0; + reg |= SDR_CTRLGRP_PHYCTRL_PHYCTRL_2_LONGIDLESAMPLECOUNT_31_20_SET( + trk_long_idle_sample_count >> + SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_LONGIDLESAMPLECOUNT_19_0_WIDTH); + writel(reg, &sdr_ctrl->phy_ctrl2); +} + +/** + * initialize_tracking() - Initialize tracking + * + * Initialize the register file with usable initial data. + */ +static void initialize_tracking(struct socfpga_sdrseq *seq) +{ + /* + * Initialize the register file with the correct data. + * Compute usable version of value in case we skip full + * computation later. + */ + writel(DIV_ROUND_UP(seq->iocfg->delay_per_opa_tap, + seq->iocfg->delay_per_dchain_tap) - 1, + &sdr_reg_file->dtaps_per_ptap); + + /* trk_sample_count */ + writel(7500, &sdr_reg_file->trk_sample_count); + + /* longidle outer loop [15:0] */ + writel((10 << 16) | (100 << 0), &sdr_reg_file->trk_longidle); + + /* + * longidle sample count [31:24] + * trfc, worst case of 933Mhz 4Gb [23:16] + * trcd, worst case [15:8] + * vfifo wait [7:0] + */ + writel((243 << 24) | (14 << 16) | (10 << 8) | (4 << 0), + &sdr_reg_file->delays); + + /* mux delay */ + if (dram_is_ddr(2)) { + writel(0, &sdr_reg_file->trk_rw_mgr_addr); + } else if (dram_is_ddr(3)) { + writel((seq->rwcfg->idle << 24) | + (seq->rwcfg->activate_1 << 16) | + (seq->rwcfg->sgle_read << 8) | + (seq->rwcfg->precharge_all << 0), + &sdr_reg_file->trk_rw_mgr_addr); + } + + writel(seq->rwcfg->mem_if_read_dqs_width, + &sdr_reg_file->trk_read_dqs_width); + + /* trefi [7:0] */ + if (dram_is_ddr(2)) { + writel(1000 << 0, &sdr_reg_file->trk_rfsh); + } else if (dram_is_ddr(3)) { + writel((seq->rwcfg->refresh_all << 24) | (1000 << 0), + &sdr_reg_file->trk_rfsh); + } +} + +int sdram_calibration_full(struct socfpga_sdr *sdr) +{ + u32 pass; + struct socfpga_sdrseq seq; + + /* + * For size reasons, this file uses hard coded addresses. + * Check if we are called with the correct address. + */ + if (sdr != (struct socfpga_sdr *)SOCFPGA_SDR_ADDRESS) + return -ENODEV; + + memset(&seq, 0, sizeof(seq)); + + seq.rwcfg = socfpga_get_sdram_rwmgr_config(); + seq.iocfg = socfpga_get_sdram_io_config(); + seq.misccfg = socfpga_get_sdram_misc_config(); + + /* Set the calibration enabled by default */ + seq.gbl.phy_debug_mode_flags |= PHY_DEBUG_ENABLE_CAL_RPT; + /* + * Only sweep all groups (regardless of fail state) by default + * Set enabled read test by default. + */ +#if DISABLE_GUARANTEED_READ + seq.gbl.phy_debug_mode_flags |= PHY_DEBUG_DISABLE_GUARANTEED_READ; +#endif + /* Initialize the register file */ + initialize_reg_file(&seq); + + /* Initialize any PHY CSR */ + initialize_hps_phy(); + + scc_mgr_initialize(); + + initialize_tracking(&seq); + + debug("%s: Preparing to start memory calibration\n", __FILE__); + + debug("%s:%d\n", __func__, __LINE__); + debug_cond(DLEVEL >= 1, + "DDR3 FULL_RATE ranks=%u cs/dimm=%u dq/dqs=%u,%u vg/dqs=%u,%u ", + seq.rwcfg->mem_number_of_ranks, + seq.rwcfg->mem_number_of_cs_per_dimm, + seq.rwcfg->mem_dq_per_read_dqs, + seq.rwcfg->mem_dq_per_write_dqs, + seq.rwcfg->mem_virtual_groups_per_read_dqs, + seq.rwcfg->mem_virtual_groups_per_write_dqs); + debug_cond(DLEVEL >= 1, + "dqs=%u,%u dq=%u dm=%u ptap_delay=%u dtap_delay=%u ", + seq.rwcfg->mem_if_read_dqs_width, + seq.rwcfg->mem_if_write_dqs_width, + seq.rwcfg->mem_data_width, seq.rwcfg->mem_data_mask_width, + seq.iocfg->delay_per_opa_tap, + seq.iocfg->delay_per_dchain_tap); + debug_cond(DLEVEL >= 1, "dtap_dqsen_delay=%u, dll=%u", + seq.iocfg->delay_per_dqs_en_dchain_tap, + seq.iocfg->dll_chain_length); + debug_cond(DLEVEL >= 1, + "max values: en_p=%u dqdqs_p=%u en_d=%u dqs_in_d=%u ", + seq.iocfg->dqs_en_phase_max, seq.iocfg->dqdqs_out_phase_max, + seq.iocfg->dqs_en_delay_max, seq.iocfg->dqs_in_delay_max); + debug_cond(DLEVEL >= 1, "io_in_d=%u io_out1_d=%u io_out2_d=%u ", + seq.iocfg->io_in_delay_max, seq.iocfg->io_out1_delay_max, + seq.iocfg->io_out2_delay_max); + debug_cond(DLEVEL >= 1, "dqs_in_reserve=%u dqs_out_reserve=%u\n", + seq.iocfg->dqs_in_reserve, seq.iocfg->dqs_out_reserve); + + hc_initialize_rom_data(); + + /* update info for sims */ + reg_file_set_stage(CAL_STAGE_NIL); + reg_file_set_group(0); + + /* + * Load global needed for those actions that require + * some dynamic calibration support. + */ + seq.dyn_calib_steps = STATIC_CALIB_STEPS; + /* + * Load global to allow dynamic selection of delay loop settings + * based on calibration mode. + */ + if (!(seq.dyn_calib_steps & CALIB_SKIP_DELAY_LOOPS)) + seq.skip_delay_mask = 0xff; + else + seq.skip_delay_mask = 0x0; + + pass = run_mem_calibrate(&seq); + debug_mem_calibrate(&seq, pass); + return pass; +} diff --git a/roms/u-boot/drivers/ddr/altera/sequencer.h b/roms/u-boot/drivers/ddr/altera/sequencer.h new file mode 100644 index 000000000..c72a683ff --- /dev/null +++ b/roms/u-boot/drivers/ddr/altera/sequencer.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright Altera Corporation (C) 2012-2015 + */ + +#ifndef _SEQUENCER_H_ +#define _SEQUENCER_H_ + +#define RW_MGR_NUM_DM_PER_WRITE_GROUP (seq->rwcfg->mem_data_mask_width \ + / seq->rwcfg->mem_if_write_dqs_width) +#define RW_MGR_NUM_TRUE_DM_PER_WRITE_GROUP ( \ + seq->rwcfg->true_mem_data_mask_width \ + / seq->rwcfg->mem_if_write_dqs_width) + +#define RW_MGR_NUM_DQS_PER_WRITE_GROUP (seq->rwcfg->mem_if_read_dqs_width \ + / seq->rwcfg->mem_if_write_dqs_width) +#define NUM_RANKS_PER_SHADOW_REG (seq->rwcfg->mem_number_of_ranks \ + / NUM_SHADOW_REGS) + +#define RW_MGR_RUN_SINGLE_GROUP_OFFSET 0x0 +#define RW_MGR_RUN_ALL_GROUPS_OFFSET 0x0400 +#define RW_MGR_RESET_READ_DATAPATH_OFFSET 0x1000 +#define RW_MGR_SET_CS_AND_ODT_MASK_OFFSET 0x1400 +#define RW_MGR_INST_ROM_WRITE_OFFSET 0x1800 +#define RW_MGR_AC_ROM_WRITE_OFFSET 0x1C00 + +#define NUM_SHADOW_REGS 1 + +#define RW_MGR_RANK_NONE 0xFF +#define RW_MGR_RANK_ALL 0x00 + +#define RW_MGR_ODT_MODE_OFF 0 +#define RW_MGR_ODT_MODE_READ_WRITE 1 + +#define NUM_CALIB_REPEAT 1 + +#define NUM_READ_TESTS 7 +#define NUM_READ_PB_TESTS 7 +#define NUM_WRITE_TESTS 15 +#define NUM_WRITE_PB_TESTS 31 + +#define PASS_ALL_BITS 1 +#define PASS_ONE_BIT 0 + +/* calibration stages */ +#define CAL_STAGE_NIL 0 +#define CAL_STAGE_VFIFO 1 +#define CAL_STAGE_WLEVEL 2 +#define CAL_STAGE_LFIFO 3 +#define CAL_STAGE_WRITES 4 +#define CAL_STAGE_FULLTEST 5 +#define CAL_STAGE_REFRESH 6 +#define CAL_STAGE_CAL_SKIPPED 7 +#define CAL_STAGE_CAL_ABORTED 8 +#define CAL_STAGE_VFIFO_AFTER_WRITES 9 + +/* calibration substages */ +#define CAL_SUBSTAGE_NIL 0 +#define CAL_SUBSTAGE_GUARANTEED_READ 1 +#define CAL_SUBSTAGE_DQS_EN_PHASE 2 +#define CAL_SUBSTAGE_VFIFO_CENTER 3 +#define CAL_SUBSTAGE_WORKING_DELAY 1 +#define CAL_SUBSTAGE_LAST_WORKING_DELAY 2 +#define CAL_SUBSTAGE_WLEVEL_COPY 3 +#define CAL_SUBSTAGE_WRITES_CENTER 1 +#define CAL_SUBSTAGE_READ_LATENCY 1 +#define CAL_SUBSTAGE_REFRESH 1 + +#define SCC_MGR_GROUP_COUNTER_OFFSET 0x0000 +#define SCC_MGR_DQS_IN_DELAY_OFFSET 0x0100 +#define SCC_MGR_DQS_EN_PHASE_OFFSET 0x0200 +#define SCC_MGR_DQS_EN_DELAY_OFFSET 0x0300 +#define SCC_MGR_DQDQS_OUT_PHASE_OFFSET 0x0400 +#define SCC_MGR_OCT_OUT1_DELAY_OFFSET 0x0500 +#define SCC_MGR_IO_OUT1_DELAY_OFFSET 0x0700 +#define SCC_MGR_IO_IN_DELAY_OFFSET 0x0900 + +/* HHP-HPS-specific versions of some commands */ +#define SCC_MGR_DQS_EN_DELAY_GATE_OFFSET 0x0600 +#define SCC_MGR_IO_OE_DELAY_OFFSET 0x0800 +#define SCC_MGR_HHP_GLOBALS_OFFSET 0x0A00 +#define SCC_MGR_HHP_RFILE_OFFSET 0x0B00 +#define SCC_MGR_AFI_CAL_INIT_OFFSET 0x0D00 + +#define SDR_PHYGRP_SCCGRP_ADDRESS (SOCFPGA_SDR_ADDRESS | 0x0) +#define SDR_PHYGRP_PHYMGRGRP_ADDRESS (SOCFPGA_SDR_ADDRESS | 0x1000) +#define SDR_PHYGRP_RWMGRGRP_ADDRESS (SOCFPGA_SDR_ADDRESS | 0x2000) +#define SDR_PHYGRP_DATAMGRGRP_ADDRESS (SOCFPGA_SDR_ADDRESS | 0x4000) +#define SDR_PHYGRP_REGFILEGRP_ADDRESS (SOCFPGA_SDR_ADDRESS | 0x4800) + +#define PHY_MGR_CAL_RESET (0) +#define PHY_MGR_CAL_SUCCESS (1) +#define PHY_MGR_CAL_FAIL (2) + +#define CALIB_SKIP_DELAY_LOOPS (1 << 0) +#define CALIB_SKIP_ALL_BITS_CHK (1 << 1) +#define CALIB_SKIP_DELAY_SWEEPS (1 << 2) +#define CALIB_SKIP_VFIFO (1 << 3) +#define CALIB_SKIP_LFIFO (1 << 4) +#define CALIB_SKIP_WLEVEL (1 << 5) +#define CALIB_SKIP_WRITES (1 << 6) +#define CALIB_SKIP_FULL_TEST (1 << 7) +#define CALIB_SKIP_ALL (CALIB_SKIP_VFIFO | \ + CALIB_SKIP_LFIFO | CALIB_SKIP_WLEVEL | \ + CALIB_SKIP_WRITES | CALIB_SKIP_FULL_TEST) +#define CALIB_IN_RTL_SIM (1 << 8) + +/* Scan chain manager command addresses */ +#define READ_SCC_OCT_OUT2_DELAY 0 +#define READ_SCC_DQ_OUT2_DELAY 0 +#define READ_SCC_DQS_IO_OUT2_DELAY 0 +#define READ_SCC_DM_IO_OUT2_DELAY 0 + +/* HHP-HPS-specific values */ +#define SCC_MGR_HHP_EXTRAS_OFFSET 0 +#define SCC_MGR_HHP_DQSE_MAP_OFFSET 1 + +/* PHY Debug mode flag constants */ +#define PHY_DEBUG_IN_DEBUG_MODE 0x00000001 +#define PHY_DEBUG_ENABLE_CAL_RPT 0x00000002 +#define PHY_DEBUG_ENABLE_MARGIN_RPT 0x00000004 +#define PHY_DEBUG_SWEEP_ALL_GROUPS 0x00000008 +#define PHY_DEBUG_DISABLE_GUARANTEED_READ 0x00000010 +#define PHY_DEBUG_ENABLE_NON_DESTRUCTIVE_CALIBRATION 0x00000020 + +struct socfpga_sdr_rw_load_manager { + u32 load_cntr0; + u32 load_cntr1; + u32 load_cntr2; + u32 load_cntr3; +}; + +struct socfpga_sdr_rw_load_jump_manager { + u32 load_jump_add0; + u32 load_jump_add1; + u32 load_jump_add2; + u32 load_jump_add3; +}; + +struct socfpga_sdr_reg_file { + u32 signature; + u32 debug_data_addr; + u32 cur_stage; + u32 fom; + u32 failing_stage; + u32 debug1; + u32 debug2; + u32 dtaps_per_ptap; + u32 trk_sample_count; + u32 trk_longidle; + u32 delays; + u32 trk_rw_mgr_addr; + u32 trk_read_dqs_width; + u32 trk_rfsh; +}; + +/* parameter variable holder */ +struct param_type { + u32 read_correct_mask; + u32 read_correct_mask_vg; + u32 write_correct_mask; + u32 write_correct_mask_vg; +}; + + +/* global variable holder */ +struct gbl_type { + uint32_t phy_debug_mode_flags; + + /* current read latency */ + + uint32_t curr_read_lat; + + /* error code */ + + uint32_t error_substage; + uint32_t error_stage; + uint32_t error_group; + + /* figure-of-merit in, figure-of-merit out */ + + uint32_t fom_in; + uint32_t fom_out; + + /*USER Number of RW Mgr NOP cycles between + write command and write data */ + uint32_t rw_wl_nop_cycles; +}; + +struct socfpga_sdr_scc_mgr { + u32 dqs_ena; + u32 dqs_io_ena; + u32 dq_ena; + u32 dm_ena; + u32 __padding1[4]; + u32 update; + u32 __padding2[7]; + u32 active_rank; +}; + +/* PHY manager configuration registers. */ +struct socfpga_phy_mgr_cfg { + u32 phy_rlat; + u32 reset_mem_stbl; + u32 mux_sel; + u32 cal_status; + u32 cal_debug_info; + u32 vfifo_rd_en_ovrd; + u32 afi_wlat; + u32 afi_rlat; +}; + +/* PHY manager command addresses. */ +struct socfpga_phy_mgr_cmd { + u32 inc_vfifo_fr; + u32 inc_vfifo_hard_phy; + u32 fifo_reset; + u32 inc_vfifo_fr_hr; + u32 inc_vfifo_qr; +}; + +struct socfpga_data_mgr { + u32 __padding1; + u32 t_wl_add; + u32 mem_t_add; + u32 t_rl_add; +}; + +/* This struct describes the controller @ SOCFPGA_SDR_ADDRESS */ +struct socfpga_sdr { + /* SDR_PHYGRP_SCCGRP_ADDRESS */ + u8 _align1[0xe00]; + /* SDR_PHYGRP_SCCGRP_ADDRESS | 0xe00 */ + struct socfpga_sdr_scc_mgr sdr_scc_mgr; + u8 _align2[0x1bc]; + /* SDR_PHYGRP_PHYMGRGRP_ADDRESS */ + struct socfpga_phy_mgr_cmd phy_mgr_cmd; + u8 _align3[0x2c]; + /* SDR_PHYGRP_PHYMGRGRP_ADDRESS | 0x40 */ + struct socfpga_phy_mgr_cfg phy_mgr_cfg; + u8 _align4[0xfa0]; + /* SDR_PHYGRP_RWMGRGRP_ADDRESS */ + u8 rwmgr_grp[0x800]; + /* SDR_PHYGRP_RWMGRGRP_ADDRESS | 0x800 */ + struct socfpga_sdr_rw_load_manager sdr_rw_load_mgr_regs; + u8 _align5[0x3f0]; + /* SDR_PHYGRP_RWMGRGRP_ADDRESS | 0xC00 */ + struct socfpga_sdr_rw_load_jump_manager sdr_rw_load_jump_mgr_regs; + u8 _align6[0x13f0]; + /* SDR_PHYGRP_DATAMGRGRP_ADDRESS */ + struct socfpga_data_mgr data_mgr; + u8 _align7[0x7f0]; + /* SDR_PHYGRP_REGFILEGRP_ADDRESS */ + struct socfpga_sdr_reg_file sdr_reg_file; + u8 _align8[0x7c8]; + /* SDR_CTRLGRP_ADDRESS */ + struct socfpga_sdr_ctrl sdr_ctrl; + u8 _align9[0xea4]; +}; + +struct socfpga_sdrseq { + const struct socfpga_sdram_rw_mgr_config *rwcfg; + const struct socfpga_sdram_io_config *iocfg; + const struct socfpga_sdram_misc_config *misccfg; + /* calibration steps requested by the rtl */ + u16 dyn_calib_steps; + /* + * To make CALIB_SKIP_DELAY_LOOPS a dynamic conditional option + * instead of static, we use boolean logic to select between + * non-skip and skip values + * + * The mask is set to include all bits when not-skipping, but is + * zero when skipping + */ + + u16 skip_delay_mask; /* mask off bits when skipping/not-skipping */ + struct gbl_type gbl; + struct param_type param; +}; + +int sdram_calibration_full(struct socfpga_sdr *sdr); +bool dram_is_ddr(const u8 ddr); + +#endif /* _SEQUENCER_H_ */ |