diff options
48 files changed, 6913 insertions, 576 deletions
@@ -649,6 +649,7 @@ libs-y += drivers/power/ \ libs-y += drivers/spi/ libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/ libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/ +libs-$(CONFIG_ALTERA_SDRAM) += drivers/ddr/altera/ libs-y += drivers/serial/ libs-y += drivers/usb/dwc3/ libs-y += drivers/usb/emul/ @@ -1019,6 +1020,15 @@ u-boot-nand.gph: u-boot.bin FORCE $(call if_changed,mkimage) @dd if=/dev/zero bs=8 count=1 2>/dev/null >> $@ +ifneq ($(CONFIG_ARCH_SOCFPGA),) +quiet_cmd_socboot = SOCBOOT $@ +cmd_socboot = cat spl/u-boot-spl-dtb.sfp spl/u-boot-spl-dtb.sfp \ + spl/u-boot-spl-dtb.sfp spl/u-boot-spl-dtb.sfp \ + u-boot-dtb.img > $@ || rm -f $@ +u-boot-with-spl-dtb.sfp: spl/u-boot-spl-dtb.sfp u-boot-dtb.img FORCE + $(call if_changed,socboot) +endif + # x86 uses a large ROM. We fill it with 0xff, put the 16-bit stuff (including # reset vector) at the top, Intel ME descriptor at the bottom, and U-Boot in # the middle. @@ -1297,6 +1307,9 @@ spl/u-boot-spl: tools prepare $(if $(CONFIG_OF_SEPARATE),dts/dt.dtb) spl/sunxi-spl.bin: spl/u-boot-spl @: +spl/u-boot-spl-dtb.sfp: spl/u-boot-spl + @: + tpl/u-boot-tpl.bin: tools prepare $(Q)$(MAKE) obj=tpl -f $(srctree)/scripts/Makefile.spl all diff --git a/arch/arm/dts/socfpga.dtsi b/arch/arm/dts/socfpga.dtsi index 9b1242025dc..e17e9f4a3ca 100644 --- a/arch/arm/dts/socfpga.dtsi +++ b/arch/arm/dts/socfpga.dtsi @@ -20,6 +20,10 @@ timer1 = &timer1; timer2 = &timer2; timer3 = &timer3; + spi0 = &qspi; + spi1 = &spi0; + spi2 = &spi1; + mmc = &mmc; }; cpus { diff --git a/arch/arm/dts/socfpga_arria5_socdk.dts b/arch/arm/dts/socfpga_arria5_socdk.dts index 1b868978728..f2b5963f594 100644 --- a/arch/arm/dts/socfpga_arria5_socdk.dts +++ b/arch/arm/dts/socfpga_arria5_socdk.dts @@ -22,13 +22,9 @@ aliases { /* this allow the ethaddr uboot environmnet variable contents - * to be added to the gmac1 device tree blob. - */ + * to be added to the gmac1 device tree blob. + */ ethernet0 = &gmac1; - - spi0 = "/spi@ff705000"; /* QSPI */ - spi1 = "/spi@fff00000"; - spi2 = "/spi@fff01000"; }; regulator_3_3v: 3-3-v-regulator { diff --git a/arch/arm/dts/socfpga_cyclone5_socdk.dts b/arch/arm/dts/socfpga_cyclone5_socdk.dts index 0b300b92be0..9650eb08777 100644 --- a/arch/arm/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/dts/socfpga_cyclone5_socdk.dts @@ -25,10 +25,6 @@ * to be added to the gmac1 device tree blob. */ ethernet0 = &gmac1; - - spi0 = "/spi@ff705000"; /* QSPI */ - spi1 = "/spi@fff00000"; - spi2 = "/spi@fff01000"; }; regulator_3_3v: 3-3-v-regulator { diff --git a/arch/arm/dts/socfpga_cyclone5_socrates.dts b/arch/arm/dts/socfpga_cyclone5_socrates.dts index ea30483e52f..00b1830485a 100644 --- a/arch/arm/dts/socfpga_cyclone5_socrates.dts +++ b/arch/arm/dts/socfpga_cyclone5_socrates.dts @@ -14,12 +14,6 @@ bootargs = "console=ttyS0,115200"; }; - aliases { - spi0 = "/spi@ff705000"; /* QSPI */ - spi1 = "/spi@fff00000"; - spi2 = "/spi@fff01000"; - }; - memory { name = "memory"; device_type = "memory"; diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index 7524ef90e49..8a745c9b1e8 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -8,5 +8,5 @@ # obj-y += misc.o timer.o reset_manager.o system_manager.o clock_manager.o \ - fpga_manager.o -obj-$(CONFIG_SPL_BUILD) += spl.o freeze_controller.o scan_manager.o + fpga_manager.o scan_manager.o +obj-$(CONFIG_SPL_BUILD) += spl.o freeze_controller.o diff --git a/arch/arm/mach-socfpga/clock_manager.c b/arch/arm/mach-socfpga/clock_manager.c index fa3b93a2572..1341df4361d 100644 --- a/arch/arm/mach-socfpga/clock_manager.c +++ b/arch/arm/mach-socfpga/clock_manager.c @@ -88,7 +88,7 @@ static void cm_write_with_phase(uint32_t value, * Ungate clocks */ -void cm_basic_init(const cm_config_t *cfg) +void cm_basic_init(const struct cm_config * const cfg) { uint32_t start, timeout; @@ -336,7 +336,7 @@ static unsigned int cm_get_main_vco_clk_hz(void) /* get the main VCO clock */ reg = readl(&clock_manager_base->main_pll.vco); - clock = CONFIG_HPS_CLK_OSC1_HZ; + clock = cm_get_osc_clk_hz(1); clock /= ((reg & CLKMGR_MAINPLLGRP_VCO_DENOM_MASK) >> CLKMGR_MAINPLLGRP_VCO_DENOM_OFFSET) + 1; clock *= ((reg & CLKMGR_MAINPLLGRP_VCO_NUMER_MASK) >> @@ -354,11 +354,11 @@ static unsigned int cm_get_per_vco_clk_hz(void) reg = (reg & CLKMGR_PERPLLGRP_VCO_SSRC_MASK) >> CLKMGR_PERPLLGRP_VCO_SSRC_OFFSET; if (reg == CLKMGR_VCO_SSRC_EOSC1) - clock = CONFIG_HPS_CLK_OSC1_HZ; + clock = cm_get_osc_clk_hz(1); else if (reg == CLKMGR_VCO_SSRC_EOSC2) - clock = CONFIG_HPS_CLK_OSC2_HZ; + clock = cm_get_osc_clk_hz(2); else if (reg == CLKMGR_VCO_SSRC_F2S) - clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ; + clock = cm_get_f2s_per_ref_clk_hz(); /* get the PER VCO clock */ reg = readl(&clock_manager_base->per_pll.vco); @@ -393,11 +393,11 @@ unsigned long cm_get_sdram_clk_hz(void) reg = (reg & CLKMGR_SDRPLLGRP_VCO_SSRC_MASK) >> CLKMGR_SDRPLLGRP_VCO_SSRC_OFFSET; if (reg == CLKMGR_VCO_SSRC_EOSC1) - clock = CONFIG_HPS_CLK_OSC1_HZ; + clock = cm_get_osc_clk_hz(1); else if (reg == CLKMGR_VCO_SSRC_EOSC2) - clock = CONFIG_HPS_CLK_OSC2_HZ; + clock = cm_get_osc_clk_hz(2); else if (reg == CLKMGR_VCO_SSRC_F2S) - clock = CONFIG_HPS_CLK_F2S_SDR_REF_HZ; + clock = cm_get_f2s_sdr_ref_clk_hz(); /* get the SDRAM VCO clock */ reg = readl(&clock_manager_base->sdr_pll.vco); @@ -459,7 +459,7 @@ unsigned int cm_get_mmc_controller_clk_hz(void) CLKMGR_PERPLLGRP_SRC_SDMMC_OFFSET; if (reg == CLKMGR_SDMMC_CLK_SRC_F2S) { - clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ; + clock = cm_get_f2s_per_ref_clk_hz(); } else if (reg == CLKMGR_SDMMC_CLK_SRC_MAIN) { clock = cm_get_main_vco_clk_hz(); @@ -489,7 +489,7 @@ unsigned int cm_get_qspi_controller_clk_hz(void) CLKMGR_PERPLLGRP_SRC_QSPI_OFFSET; if (reg == CLKMGR_QSPI_CLK_SRC_F2S) { - clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ; + clock = cm_get_f2s_per_ref_clk_hz(); } else if (reg == CLKMGR_QSPI_CLK_SRC_MAIN) { clock = cm_get_main_vco_clk_hz(); @@ -524,10 +524,10 @@ static void cm_print_clock_quick_summary(void) { printf("MPU %10ld kHz\n", cm_get_mpu_clk_hz() / 1000); printf("DDR %10ld kHz\n", cm_get_sdram_clk_hz() / 1000); - printf("EOSC1 %8d kHz\n", CONFIG_HPS_CLK_OSC1_HZ / 1000); - printf("EOSC2 %8d kHz\n", CONFIG_HPS_CLK_OSC2_HZ / 1000); - printf("F2S_SDR_REF %8d kHz\n", CONFIG_HPS_CLK_F2S_SDR_REF_HZ / 1000); - printf("F2S_PER_REF %8d kHz\n", CONFIG_HPS_CLK_F2S_PER_REF_HZ / 1000); + printf("EOSC1 %8d kHz\n", cm_get_osc_clk_hz(1) / 1000); + printf("EOSC2 %8d kHz\n", cm_get_osc_clk_hz(2) / 1000); + printf("F2S_SDR_REF %8d kHz\n", cm_get_f2s_sdr_ref_clk_hz() / 1000); + printf("F2S_PER_REF %8d kHz\n", cm_get_f2s_per_ref_clk_hz() / 1000); printf("MMC %8d kHz\n", cm_get_mmc_controller_clk_hz() / 1000); printf("QSPI %8d kHz\n", cm_get_qspi_controller_clk_hz() / 1000); printf("UART %8d kHz\n", cm_get_l4_sp_clk_hz() / 1000); diff --git a/arch/arm/mach-socfpga/include/mach/clock_manager.h b/arch/arm/mach-socfpga/include/mach/clock_manager.h index 54497261802..2675951a3e2 100644 --- a/arch/arm/mach-socfpga/include/mach/clock_manager.h +++ b/arch/arm/mach-socfpga/include/mach/clock_manager.h @@ -15,9 +15,15 @@ unsigned int cm_get_l4_sp_clk_hz(void); unsigned int cm_get_mmc_controller_clk_hz(void); unsigned int cm_get_qspi_controller_clk_hz(void); unsigned int cm_get_spi_controller_clk_hz(void); +const unsigned int cm_get_osc_clk_hz(const int osc); +const unsigned int cm_get_f2s_per_ref_clk_hz(void); +const unsigned int cm_get_f2s_sdr_ref_clk_hz(void); + +/* Clock configuration accessors */ +const struct cm_config * const cm_get_default_config(void); #endif -typedef struct { +struct cm_config { /* main group */ uint32_t main_vco_base; uint32_t mpuclk; @@ -49,9 +55,9 @@ typedef struct { uint32_t ddr2xdqsclk; uint32_t ddrdqclk; uint32_t s2fuser2clk; -} cm_config_t; +}; -extern void cm_basic_init(const cm_config_t *cfg); +void cm_basic_init(const struct cm_config * const cfg); struct socfpga_clock_manager_main_pll { u32 vco; diff --git a/arch/arm/mach-socfpga/include/mach/reset_manager.h b/arch/arm/mach-socfpga/include/mach/reset_manager.h index d63a2850912..8e59578f374 100644 --- a/arch/arm/mach-socfpga/include/mach/reset_manager.h +++ b/arch/arm/mach-socfpga/include/mach/reset_manager.h @@ -12,12 +12,8 @@ void reset_deassert_peripherals_handoff(void); void socfpga_bridges_reset(int enable); -void socfpga_emac_reset(int enable); -void socfpga_watchdog_reset(void); -void socfpga_spim_enable(void); -void socfpga_uart0_enable(void); -void socfpga_sdram_enable(void); -void socfpga_osc1timer_enable(void); +void socfpga_per_reset(u32 reset, int set); +void socfpga_per_reset_all(void); struct socfpga_reset_manager { u32 status; @@ -28,6 +24,8 @@ struct socfpga_reset_manager { u32 per_mod_reset; u32 per2_mod_reset; u32 brg_mod_reset; + u32 misc_mod_reset; + u32 tstscratch; }; #if defined(CONFIG_SOCFPGA_VIRTUAL_TARGET) @@ -36,13 +34,47 @@ struct socfpga_reset_manager { #define RSTMGR_CTRL_SWWARMRSTREQ_LSB 1 #endif -#define RSTMGR_PERMODRST_EMAC0_LSB 0 -#define RSTMGR_PERMODRST_EMAC1_LSB 1 -#define RSTMGR_PERMODRST_L4WD0_LSB 6 -#define RSTMGR_PERMODRST_OSC1TIMER0_LSB 8 -#define RSTMGR_PERMODRST_UART0_LSB 16 -#define RSTMGR_PERMODRST_SPIM0_LSB 18 -#define RSTMGR_PERMODRST_SPIM1_LSB 19 -#define RSTMGR_PERMODRST_SDR_LSB 29 +/* + * Define a reset identifier, from which a permodrst bank ID + * and reset ID can be extracted using the subsequent macros + * RSTMGR_RESET() and RSTMGR_BANK(). + */ +#define RSTMGR_BANK_OFFSET 8 +#define RSTMGR_BANK_MASK 0x7 +#define RSTMGR_RESET_OFFSET 0 +#define RSTMGR_RESET_MASK 0x1f +#define RSTMGR_DEFINE(_bank, _offset) \ + ((_bank) << RSTMGR_BANK_OFFSET) | ((_offset) << RSTMGR_RESET_OFFSET) + +/* Extract reset ID from the reset identifier. */ +#define RSTMGR_RESET(_reset) \ + (((_reset) >> RSTMGR_RESET_OFFSET) & RSTMGR_RESET_MASK) + +/* Extract bank ID from the reset identifier. */ +#define RSTMGR_BANK(_reset) \ + (((_reset) >> RSTMGR_BANK_OFFSET) & RSTMGR_BANK_MASK) + +/* + * SocFPGA Cyclone V/Arria V reset IDs, bank mapping is as follows: + * 0 ... mpumodrst + * 1 ... permodrst + * 2 ... per2modrst + * 3 ... brgmodrst + * 4 ... miscmodrst + */ +#define RSTMGR_EMAC0 RSTMGR_DEFINE(1, 0) +#define RSTMGR_EMAC1 RSTMGR_DEFINE(1, 1) +#define RSTMGR_L4WD0 RSTMGR_DEFINE(1, 6) +#define RSTMGR_OSC1TIMER0 RSTMGR_DEFINE(1, 8) +#define RSTMGR_UART0 RSTMGR_DEFINE(1, 16) +#define RSTMGR_SPIM0 RSTMGR_DEFINE(1, 18) +#define RSTMGR_SPIM1 RSTMGR_DEFINE(1, 19) +#define RSTMGR_QSPI RSTMGR_DEFINE(0, 5) +#define RSTMGR_SDMMC RSTMGR_DEFINE(0, 22) +#define RSTMGR_DMA RSTMGR_DEFINE(0, 28) +#define RSTMGR_SDR RSTMGR_DEFINE(1, 29) + +/* Create a human-readable reference to SoCFPGA reset. */ +#define SOCFPGA_RESET(_name) RSTMGR_##_name #endif /* _RESET_MANAGER_H_ */ diff --git a/arch/arm/mach-socfpga/include/mach/scan_manager.h b/arch/arm/mach-socfpga/include/mach/scan_manager.h index 1155fd3decc..5ca68435575 100644 --- a/arch/arm/mach-socfpga/include/mach/scan_manager.h +++ b/arch/arm/mach-socfpga/include/mach/scan_manager.h @@ -17,77 +17,10 @@ struct socfpga_scan_manager { u32 fifo_quad_byte; }; -/* - * Shift count to get number of IO scan chain data in granularity - * of 128-bit ( N / 128 ) - */ -#define IO_SCAN_CHAIN_128BIT_SHIFT 7 - -/* - * Mask to get residual IO scan chain data in - * granularity of 128-bit ( N mod 128 ) - */ -#define IO_SCAN_CHAIN_128BIT_MASK 0x7F - -/* - * Shift count to get number of IO scan chain - * data in granularity of 32-bit ( N / 32 ) - */ -#define IO_SCAN_CHAIN_32BIT_SHIFT 5 - -/* - * Mask to get residual IO scan chain data in - * granularity of 32-bit ( N mod 32 ) - */ -#define IO_SCAN_CHAIN_32BIT_MASK 0x1F - -/* Byte mask */ -#define IO_SCAN_CHAIN_BYTE_MASK 0xFF - -/* 24-bits (3 bytes) IO scan chain payload definition */ -#define IO_SCAN_CHAIN_PAYLOAD_24BIT 24 - -/* - * Maximum length of TDI_TDO packet payload is 128 bits, - * represented by (length - 1) in TDI_TDO header - */ -#define TDI_TDO_MAX_PAYLOAD 127 - -/* TDI_TDO packet header for IO scan chain program */ -#define TDI_TDO_HEADER_FIRST_BYTE 0x80 - -/* Position of second command byte for TDI_TDO packet */ -#define TDI_TDO_HEADER_SECOND_BYTE_SHIFT 8 - -/* - * Maximum polling loop to wait for IO scan chain engine - * becomes idle to prevent infinite loop - */ -#define SCAN_MAX_DELAY 100 - -#define SCANMGR_STAT_ACTIVE_GET(x) (((x) & 0x80000000) >> 31) -#define SCANMGR_STAT_WFIFOCNT_GET(x) (((x) & 0x70000000) >> 28) - -/* - * Program HPS IO Scan Chain - * io_scan_chain_id - IO scan chain ID - * io_scan_chain_len_in_bits - IO scan chain length in bits - * iocsr_scan_chain - IO scan chain table - */ -uint32_t scan_mgr_io_scan_chain_prg( - uint32_t io_scan_chain_id, - uint32_t io_scan_chain_len_in_bits, - const uint32_t *iocsr_scan_chain); - -extern const uint32_t iocsr_scan_chain0_table[ - ((CONFIG_HPS_IOCSR_SCANCHAIN0_LENGTH / 32) + 1)]; -extern const uint32_t iocsr_scan_chain1_table[ - ((CONFIG_HPS_IOCSR_SCANCHAIN1_LENGTH / 32) + 1)]; -extern const uint32_t iocsr_scan_chain2_table[ - ((CONFIG_HPS_IOCSR_SCANCHAIN2_LENGTH / 32) + 1)]; -extern const uint32_t iocsr_scan_chain3_table[ - ((CONFIG_HPS_IOCSR_SCANCHAIN3_LENGTH / 32) + 1)]; - int scan_mgr_configure_iocsr(void); +u32 scan_mgr_get_fpga_id(void); +int iocsr_get_config_table(const unsigned int chain_id, + const unsigned long **table, + unsigned int *table_len); #endif /* _SCAN_MANAGER_H_ */ diff --git a/arch/arm/mach-socfpga/include/mach/sdram.h b/arch/arm/mach-socfpga/include/mach/sdram.h index 4f6489dff67..f12bb846613 100644 --- a/arch/arm/mach-socfpga/include/mach/sdram.h +++ b/arch/arm/mach-socfpga/include/mach/sdram.h @@ -1,19 +1,436 @@ /* - * Copyright (C) 2015 Marek Vasut <marex@denx.de> - * - * FIXME: This file contains temporary stub functions and is here - * only until these functions are properly merged into - * mainline. + * Copyright Altera Corporation (C) 2014-2015 * * SPDX-License-Identifier: GPL-2.0+ */ +#ifndef _SDRAM_H_ +#define _SDRAM_H_ + +#ifndef __ASSEMBLY__ + +unsigned long sdram_calculate_size(void); +int sdram_mmr_init_full(unsigned int sdr_phy_reg); +int sdram_calibration_full(void); + +const struct socfpga_sdram_config *socfpga_get_sdram_config(void); + +void socfpga_get_seq_ac_init(const u32 **init, unsigned int *nelem); +void socfpga_get_seq_inst_init(const u32 **init, unsigned int *nelem); +const struct socfpga_sdram_rw_mgr_config *socfpga_get_sdram_rwmgr_config(void); +const struct socfpga_sdram_io_config *socfpga_get_sdram_io_config(void); +const struct socfpga_sdram_misc_config *socfpga_get_sdram_misc_config(void); + +#define SDR_CTRLGRP_ADDRESS (SOCFPGA_SDR_ADDRESS | 0x5000) + +struct socfpga_sdr_ctrl { + u32 ctrl_cfg; + u32 dram_timing1; + u32 dram_timing2; + u32 dram_timing3; + u32 dram_timing4; /* 0x10 */ + u32 lowpwr_timing; + u32 dram_odt; + u32 __padding0[4]; + u32 dram_addrw; /* 0x2c */ + u32 dram_if_width; /* 0x30 */ + u32 dram_dev_width; + u32 dram_sts; + u32 dram_intr; + u32 sbe_count; /* 0x40 */ + u32 dbe_count; + u32 err_addr; + u32 drop_count; + u32 drop_addr; /* 0x50 */ + u32 lowpwr_eq; + u32 lowpwr_ack; + u32 static_cfg; + u32 ctrl_width; /* 0x60 */ + u32 cport_width; + u32 cport_wmap; + u32 cport_rmap; + u32 rfifo_cmap; /* 0x70 */ + u32 wfifo_cmap; + u32 cport_rdwr; + u32 port_cfg; + u32 fpgaport_rst; /* 0x80 */ + u32 __padding1; + u32 fifo_cfg; + u32 protport_default; + u32 prot_rule_addr; /* 0x90 */ + u32 prot_rule_id; + u32 prot_rule_data; + u32 prot_rule_rdwr; + u32 __padding2[3]; + u32 mp_priority; /* 0xac */ + u32 mp_weight0; /* 0xb0 */ + u32 mp_weight1; + u32 mp_weight2; + u32 mp_weight3; + u32 mp_pacing0; /* 0xc0 */ + u32 mp_pacing1; + u32 mp_pacing2; + u32 mp_pacing3; + u32 mp_threshold0; /* 0xd0 */ + u32 mp_threshold1; + u32 mp_threshold2; + u32 __padding3[29]; + u32 phy_ctrl0; /* 0x150 */ + u32 phy_ctrl1; + u32 phy_ctrl2; +}; + +/* SDRAM configuration structure for the SPL. */ +struct socfpga_sdram_config { + u32 ctrl_cfg; + u32 dram_timing1; + u32 dram_timing2; + u32 dram_timing3; + u32 dram_timing4; + u32 lowpwr_timing; + u32 dram_odt; + u32 dram_addrw; + u32 dram_if_width; + u32 dram_dev_width; + u32 dram_intr; + u32 lowpwr_eq; + u32 static_cfg; + u32 ctrl_width; + u32 cport_width; + u32 cport_wmap; + u32 cport_rmap; + u32 rfifo_cmap; + u32 wfifo_cmap; + u32 cport_rdwr; + u32 port_cfg; + u32 fpgaport_rst; + u32 fifo_cfg; + u32 mp_priority; + u32 mp_weight0; + u32 mp_weight1; + u32 mp_weight2; + u32 mp_weight3; + u32 mp_pacing0; + u32 mp_pacing1; + u32 mp_pacing2; + u32 mp_pacing3; + u32 mp_threshold0; + u32 mp_threshold1; + u32 mp_threshold2; + u32 phy_ctrl0; +}; + +struct socfpga_sdram_rw_mgr_config { + u8 activate_0_and_1; + u8 activate_0_and_1_wait1; + u8 activate_0_and_1_wait2; + u8 activate_1; + u8 clear_dqs_enable; + u8 guaranteed_read; + u8 guaranteed_read_cont; + u8 guaranteed_write; + u8 guaranteed_write_wait0; + u8 guaranteed_write_wait1; + u8 guaranteed_write_wait2; + u8 guaranteed_write_wait3; + u8 idle; + u8 idle_loop1; + u8 idle_loop2; + u8 init_reset_0_cke_0; + u8 init_reset_1_cke_0; + u8 lfsr_wr_rd_bank_0; + u8 lfsr_wr_rd_bank_0_data; + u8 lfsr_wr_rd_bank_0_dqs; + u8 lfsr_wr_rd_bank_0_nop; + u8 lfsr_wr_rd_bank_0_wait; + u8 lfsr_wr_rd_bank_0_wl_1; + u8 lfsr_wr_rd_dm_bank_0; + u8 lfsr_wr_rd_dm_bank_0_data; + u8 lfsr_wr_rd_dm_bank_0_dqs; + u8 lfsr_wr_rd_dm_bank_0_nop; + u8 lfsr_wr_rd_dm_bank_0_wait; + u8 lfsr_wr_rd_dm_bank_0_wl_1; + u8 mrs0_dll_reset; + u8 mrs0_dll_reset_mirr; + u8 mrs0_user; + u8 mrs0_user_mirr; + u8 mrs1; + u8 mrs1_mirr; + u8 mrs2; + u8 mrs2_mirr; + u8 mrs3; + u8 mrs3_mirr; + u8 precharge_all; + u8 read_b2b; + u8 read_b2b_wait1; + u8 read_b2b_wait2; + u8 refresh_all; + u8 rreturn; + u8 sgle_read; + u8 zqcl; + + u8 true_mem_data_mask_width; + u8 mem_address_mirroring; + u8 mem_data_mask_width; + u8 mem_data_width; + u8 mem_dq_per_read_dqs; + u8 mem_dq_per_write_dqs; + u8 mem_if_read_dqs_width; + u8 mem_if_write_dqs_width; + u8 mem_number_of_cs_per_dimm; + u8 mem_number_of_ranks; + u8 mem_virtual_groups_per_read_dqs; + u8 mem_virtual_groups_per_write_dqs; +}; + +struct socfpga_sdram_io_config { + u16 delay_per_opa_tap; + u8 delay_per_dchain_tap; + u8 delay_per_dqs_en_dchain_tap; + u8 dll_chain_length; + u8 dqdqs_out_phase_max; + u8 dqs_en_delay_max; + u8 dqs_en_delay_offset; + u8 dqs_en_phase_max; + u8 dqs_in_delay_max; + u8 dqs_in_reserve; + u8 dqs_out_reserve; + u8 io_in_delay_max; + u8 io_out1_delay_max; + u8 io_out2_delay_max; + u8 shift_dqs_en_when_shift_dqs; +}; + +struct socfpga_sdram_misc_config { + u32 reg_file_init_seq_signature; + u8 afi_rate_ratio; + u8 calib_lfifo_offset; + u8 calib_vfifo_offset; + u8 enable_super_quick_calibration; + u8 max_latency_count_width; + u8 read_valid_fifo_size; + u8 tinit_cntr0_val; + u8 tinit_cntr1_val; + u8 tinit_cntr2_val; + u8 treset_cntr0_val; + u8 treset_cntr1_val; + u8 treset_cntr2_val; +}; -#ifndef __ARCH_SDRAM_H__ -#define __ARCH_SDRAM_H__ +#define SDR_CTRLGRP_CTRLCFG_NODMPINS_LSB 23 +#define SDR_CTRLGRP_CTRLCFG_NODMPINS_MASK 0x00800000 +#define SDR_CTRLGRP_CTRLCFG_DQSTRKEN_LSB 22 +#define SDR_CTRLGRP_CTRLCFG_DQSTRKEN_MASK 0x00400000 +#define SDR_CTRLGRP_CTRLCFG_STARVELIMIT_LSB 16 +#define SDR_CTRLGRP_CTRLCFG_STARVELIMIT_MASK 0x003f0000 +#define SDR_CTRLGRP_CTRLCFG_REORDEREN_LSB 15 +#define SDR_CTRLGRP_CTRLCFG_REORDEREN_MASK 0x00008000 +#define SDR_CTRLGRP_CTRLCFG_ECCCORREN_LSB 11 +#define SDR_CTRLGRP_CTRLCFG_ECCCORREN_MASK 0x00000800 +#define SDR_CTRLGRP_CTRLCFG_ECCEN_LSB 10 +#define SDR_CTRLGRP_CTRLCFG_ECCEN_MASK 0x00000400 +#define SDR_CTRLGRP_CTRLCFG_ADDRORDER_LSB 8 +#define SDR_CTRLGRP_CTRLCFG_ADDRORDER_MASK 0x00000300 +#define SDR_CTRLGRP_CTRLCFG_MEMBL_LSB 3 +#define SDR_CTRLGRP_CTRLCFG_MEMBL_MASK 0x000000f8 +#define SDR_CTRLGRP_CTRLCFG_MEMTYPE_LSB 0 +#define SDR_CTRLGRP_CTRLCFG_MEMTYPE_MASK 0x00000007 +/* Register template: sdr::ctrlgrp::dramtiming1 */ +#define SDR_CTRLGRP_DRAMTIMING1_TRFC_LSB 24 +#define SDR_CTRLGRP_DRAMTIMING1_TRFC_MASK 0xff000000 +#define SDR_CTRLGRP_DRAMTIMING1_TFAW_LSB 18 +#define SDR_CTRLGRP_DRAMTIMING1_TFAW_MASK 0x00fc0000 +#define SDR_CTRLGRP_DRAMTIMING1_TRRD_LSB 14 +#define SDR_CTRLGRP_DRAMTIMING1_TRRD_MASK 0x0003c000 +#define SDR_CTRLGRP_DRAMTIMING1_TCL_LSB 9 +#define SDR_CTRLGRP_DRAMTIMING1_TCL_MASK 0x00003e00 +#define SDR_CTRLGRP_DRAMTIMING1_TAL_LSB 4 +#define SDR_CTRLGRP_DRAMTIMING1_TAL_MASK 0x000001f0 +#define SDR_CTRLGRP_DRAMTIMING1_TCWL_LSB 0 +#define SDR_CTRLGRP_DRAMTIMING1_TCWL_MASK 0x0000000f +/* Register template: sdr::ctrlgrp::dramtiming2 */ +#define SDR_CTRLGRP_DRAMTIMING2_TWTR_LSB 25 +#define SDR_CTRLGRP_DRAMTIMING2_TWTR_MASK 0x1e000000 +#define SDR_CTRLGRP_DRAMTIMING2_TWR_LSB 21 +#define SDR_CTRLGRP_DRAMTIMING2_TWR_MASK 0x01e00000 +#define SDR_CTRLGRP_DRAMTIMING2_TRP_LSB 17 +#define SDR_CTRLGRP_DRAMTIMING2_TRP_MASK 0x001e0000 +#define SDR_CTRLGRP_DRAMTIMING2_TRCD_LSB 13 +#define SDR_CTRLGRP_DRAMTIMING2_TRCD_MASK 0x0001e000 +#define SDR_CTRLGRP_DRAMTIMING2_TREFI_LSB 0 +#define SDR_CTRLGRP_DRAMTIMING2_TREFI_MASK 0x00001fff +/* Register template: sdr::ctrlgrp::dramtiming3 */ +#define SDR_CTRLGRP_DRAMTIMING3_TCCD_LSB 19 +#define SDR_CTRLGRP_DRAMTIMING3_TCCD_MASK 0x00780000 +#define SDR_CTRLGRP_DRAMTIMING3_TMRD_LSB 15 +#define SDR_CTRLGRP_DRAMTIMING3_TMRD_MASK 0x00078000 +#define SDR_CTRLGRP_DRAMTIMING3_TRC_LSB 9 +#define SDR_CTRLGRP_DRAMTIMING3_TRC_MASK 0x00007e00 +#define SDR_CTRLGRP_DRAMTIMING3_TRAS_LSB 4 +#define SDR_CTRLGRP_DRAMTIMING3_TRAS_MASK 0x000001f0 +#define SDR_CTRLGRP_DRAMTIMING3_TRTP_LSB 0 +#define SDR_CTRLGRP_DRAMTIMING3_TRTP_MASK 0x0000000f +/* Register template: sdr::ctrlgrp::dramtiming4 */ +#define SDR_CTRLGRP_DRAMTIMING4_MINPWRSAVECYCLES_LSB 20 +#define SDR_CTRLGRP_DRAMTIMING4_MINPWRSAVECYCLES_MASK 0x00f00000 +#define SDR_CTRLGRP_DRAMTIMING4_PWRDOWNEXIT_LSB 10 +#define SDR_CTRLGRP_DRAMTIMING4_PWRDOWNEXIT_MASK 0x000ffc00 +#define SDR_CTRLGRP_DRAMTIMING4_SELFRFSHEXIT_LSB 0 +#define SDR_CTRLGRP_DRAMTIMING4_SELFRFSHEXIT_MASK 0x000003ff +/* Register template: sdr::ctrlgrp::lowpwrtiming */ +#define SDR_CTRLGRP_LOWPWRTIMING_CLKDISABLECYCLES_LSB 16 +#define SDR_CTRLGRP_LOWPWRTIMING_CLKDISABLECYCLES_MASK 0x000f0000 +#define SDR_CTRLGRP_LOWPWRTIMING_AUTOPDCYCLES_LSB 0 +#define SDR_CTRLGRP_LOWPWRTIMING_AUTOPDCYCLES_MASK 0x0000ffff +/* Register template: sdr::ctrlgrp::dramaddrw */ +#define SDR_CTRLGRP_DRAMADDRW_CSBITS_LSB 13 +#define SDR_CTRLGRP_DRAMADDRW_CSBITS_MASK 0x0000e000 +#define SDR_CTRLGRP_DRAMADDRW_BANKBITS_LSB 10 +#define SDR_CTRLGRP_DRAMADDRW_BANKBITS_MASK 0x00001c00 +#define SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB 5 +#define SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK 0x000003e0 +#define SDR_CTRLGRP_DRAMADDRW_COLBITS_LSB 0 +#define SDR_CTRLGRP_DRAMADDRW_COLBITS_MASK 0x0000001f +/* Register template: sdr::ctrlgrp::dramifwidth */ +#define SDR_CTRLGRP_DRAMIFWIDTH_IFWIDTH_LSB 0 +#define SDR_CTRLGRP_DRAMIFWIDTH_IFWIDTH_MASK 0x000000ff +/* Register template: sdr::ctrlgrp::dramdevwidth */ +#define SDR_CTRLGRP_DRAMDEVWIDTH_DEVWIDTH_LSB 0 +#define SDR_CTRLGRP_DRAMDEVWIDTH_DEVWIDTH_MASK 0x0000000f +/* Register template: sdr::ctrlgrp::dramintr */ +#define SDR_CTRLGRP_DRAMINTR_INTREN_LSB 0 +#define SDR_CTRLGRP_DRAMINTR_INTREN_MASK 0x00000001 +#define SDR_CTRLGRP_LOWPWREQ_SELFRFSHMASK_LSB 4 +#define SDR_CTRLGRP_LOWPWREQ_SELFRFSHMASK_MASK 0x00000030 +/* Register template: sdr::ctrlgrp::staticcfg */ +#define SDR_CTRLGRP_STATICCFG_APPLYCFG_LSB 3 +#define SDR_CTRLGRP_STATICCFG_APPLYCFG_MASK 0x00000008 +#define SDR_CTRLGRP_STATICCFG_USEECCASDATA_LSB 2 +#define SDR_CTRLGRP_STATICCFG_USEECCASDATA_MASK 0x00000004 +#define SDR_CTRLGRP_STATICCFG_MEMBL_LSB 0 +#define SDR_CTRLGRP_STATICCFG_MEMBL_MASK 0x00000003 +/* Register template: sdr::ctrlgrp::ctrlwidth */ +#define SDR_CTRLGRP_CTRLWIDTH_CTRLWIDTH_LSB 0 +#define SDR_CTRLGRP_CTRLWIDTH_CTRLWIDTH_MASK 0x00000003 +/* Register template: sdr::ctrlgrp::cportwidth */ +#define SDR_CTRLGRP_CPORTWIDTH_CMDPORTWIDTH_LSB 0 +#define SDR_CTRLGRP_CPORTWIDTH_CMDPORTWIDTH_MASK 0x000fffff +/* Register template: sdr::ctrlgrp::cportwmap */ +#define SDR_CTRLGRP_CPORTWMAP_CPORTWFIFOMAP_LSB 0 +#define SDR_CTRLGRP_CPORTWMAP_CPORTWFIFOMAP_MASK 0x3fffffff +/* Register template: sdr::ctrlgrp::cportrmap */ +#define SDR_CTRLGRP_CPORTRMAP_CPORTRFIFOMAP_LSB 0 +#define SDR_CTRLGRP_CPORTRMAP_CPORTRFIFOMAP_MASK 0x3fffffff +/* Register template: sdr::ctrlgrp::rfifocmap */ +#define SDR_CTRLGRP_RFIFOCMAP_RFIFOCPORTMAP_LSB 0 +#define SDR_CTRLGRP_RFIFOCMAP_RFIFOCPORTMAP_MASK 0x00ffffff +/* Register template: sdr::ctrlgrp::wfifocmap */ +#define SDR_CTRLGRP_WFIFOCMAP_WFIFOCPORTMAP_LSB 0 +#define SDR_CTRLGRP_WFIFOCMAP_WFIFOCPORTMAP_MASK 0x00ffffff +/* Register template: sdr::ctrlgrp::cportrdwr */ +#define SDR_CTRLGRP_CPORTRDWR_CPORTRDWR_LSB 0 +#define SDR_CTRLGRP_CPORTRDWR_CPORTRDWR_MASK 0x000fffff +/* Register template: sdr::ctrlgrp::portcfg */ +#define SDR_CTRLGRP_PORTCFG_AUTOPCHEN_LSB 10 +#define SDR_CTRLGRP_PORTCFG_AUTOPCHEN_MASK 0x000ffc00 +#define SDR_CTRLGRP_PORTCFG_PORTPROTOCOL_LSB 0 +#define SDR_CTRLGRP_PORTCFG_PORTPROTOCOL_MASK 0x000003ff +/* Register template: sdr::ctrlgrp::fifocfg */ +#define SDR_CTRLGRP_FIFOCFG_INCSYNC_LSB 10 +#define SDR_CTRLGRP_FIFOCFG_INCSYNC_MASK 0x00000400 +#define SDR_CTRLGRP_FIFOCFG_SYNCMODE_LSB 0 +#define SDR_CTRLGRP_FIFOCFG_SYNCMODE_MASK 0x000003ff +/* Register template: sdr::ctrlgrp::mppriority */ +#define SDR_CTRLGRP_MPPRIORITY_USERPRIORITY_LSB 0 +#define SDR_CTRLGRP_MPPRIORITY_USERPRIORITY_MASK 0x3fffffff +/* Register template: sdr::ctrlgrp::mpweight::mpweight_0 */ +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_0_STATICWEIGHT_31_0_LSB 0 +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_0_STATICWEIGHT_31_0_MASK 0xffffffff +/* Register template: sdr::ctrlgrp::mpweight::mpweight_1 */ +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_1_SUMOFWEIGHTS_13_0_LSB 18 +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_1_SUMOFWEIGHTS_13_0_MASK 0xfffc0000 +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_1_STATICWEIGHT_49_32_LSB 0 +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_1_STATICWEIGHT_49_32_MASK 0x0003ffff +/* Register template: sdr::ctrlgrp::mpweight::mpweight_2 */ +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_2_SUMOFWEIGHTS_45_14_LSB 0 +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_2_SUMOFWEIGHTS_45_14_MASK 0xffffffff +/* Register template: sdr::ctrlgrp::mpweight::mpweight_3 */ +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_3_SUMOFWEIGHTS_63_46_LSB 0 +#define SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_3_SUMOFWEIGHTS_63_46_MASK 0x0003ffff +/* Register template: sdr::ctrlgrp::mppacing::mppacing_0 */ +#define SDR_CTRLGRP_MPPACING_MPPACING_0_THRESHOLD1_31_0_LSB 0 +#define SDR_CTRLGRP_MPPACING_MPPACING_0_THRESHOLD1_31_0_MASK 0xffffffff +/* Register template: sdr::ctrlgrp::mppacing::mppacing_1 */ +#define SDR_CTRLGRP_MPPACING_MPPACING_1_THRESHOLD2_3_0_LSB 28 +#define SDR_CTRLGRP_MPPACING_MPPACING_1_THRESHOLD2_3_0_MASK 0xf0000000 +#define SDR_CTRLGRP_MPPACING_MPPACING_1_THRESHOLD1_59_32_LSB 0 +#define SDR_CTRLGRP_MPPACING_MPPACING_1_THRESHOLD1_59_32_MASK 0x0fffffff +/* Register template: sdr::ctrlgrp::mppacing::mppacing_2 */ +#define SDR_CTRLGRP_MPPACING_MPPACING_2_THRESHOLD2_35_4_LSB 0 +#define SDR_CTRLGRP_MPPACING_MPPACING_2_THRESHOLD2_35_4_MASK 0xffffffff +/* Register template: sdr::ctrlgrp::mppacing::mppacing_3 */ +#define SDR_CTRLGRP_MPPACING_MPPACING_3_THRESHOLD2_59_36_LSB 0 +#define SDR_CTRLGRP_MPPACING_MPPACING_3_THRESHOLD2_59_36_MASK 0x00ffffff +/* Register template: sdr::ctrlgrp::mpthresholdrst::mpthresholdrst_0 */ +#define \ +SDR_CTRLGRP_MPTHRESHOLDRST_0_THRESHOLDRSTCYCLES_31_0_LSB 0 +#define \ +SDR_CTRLGRP_MPTHRESHOLDRST_0_THRESHOLDRSTCYCLES_31_0_MASK \ +0xffffffff +/* Register template: sdr::ctrlgrp::mpthresholdrst::mpthresholdrst_1 */ +#define \ +SDR_CTRLGRP_MPTHRESHOLDRST_1_THRESHOLDRSTCYCLES_63_32_LSB 0 +#define \ +SDR_CTRLGRP_MPTHRESHOLDRST_1_THRESHOLDRSTCYCLES_63_32_MASK \ +0xffffffff +/* Register template: sdr::ctrlgrp::mpthresholdrst::mpthresholdrst_2 */ +#define \ +SDR_CTRLGRP_MPTHRESHOLDRST_2_THRESHOLDRSTCYCLES_79_64_LSB 0 +#define \ +SDR_CTRLGRP_MPTHRESHOLDRST_2_THRESHOLDRSTCYCLES_79_64_MASK \ +0x0000ffff +/* Register template: sdr::ctrlgrp::remappriority */ +#define SDR_CTRLGRP_REMAPPRIORITY_PRIORITYREMAP_LSB 0 +#define SDR_CTRLGRP_REMAPPRIORITY_PRIORITYREMAP_MASK 0x000000ff +/* Register template: sdr::ctrlgrp::phyctrl::phyctrl_0 */ +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_LSB 12 +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_WIDTH 20 +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_SET(x) \ + (((x) << 12) & 0xfffff000) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_ADDLATSEL_SET(x) \ + (((x) << 10) & 0x00000c00) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQSLOGICDELAYEN_SET(x) \ + (((x) << 6) & 0x000000c0) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_RESETDELAYEN_SET(x) \ + (((x) << 8) & 0x00000100) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_LPDDRDIS_SET(x) \ + (((x) << 9) & 0x00000200) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQSDELAYEN_SET(x) \ + (((x) << 4) & 0x00000030) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQDELAYEN_SET(x) \ + (((x) << 2) & 0x0000000c) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_ACDELAYEN_SET(x) \ + (((x) << 0) & 0x00000003) +/* Register template: sdr::ctrlgrp::phyctrl::phyctrl_1 */ +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_LONGIDLESAMPLECOUNT_19_0_WIDTH 20 +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_LONGIDLESAMPLECOUNT_19_0_SET(x) \ + (((x) << 12) & 0xfffff000) +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_SAMPLECOUNT_31_20_SET(x) \ + (((x) << 0) & 0x00000fff) +/* Register template: sdr::ctrlgrp::phyctrl::phyctrl_2 */ +#define SDR_CTRLGRP_PHYCTRL_PHYCTRL_2_LONGIDLESAMPLECOUNT_31_20_SET(x) \ + (((x) << 0) & 0x00000fff) +/* Register template: sdr::ctrlgrp::dramodt */ +#define SDR_CTRLGRP_DRAMODT_READ_LSB 4 +#define SDR_CTRLGRP_DRAMODT_READ_MASK 0x000000f0 +#define SDR_CTRLGRP_DRAMODT_WRITE_LSB 0 +#define SDR_CTRLGRP_DRAMODT_WRITE_MASK 0x0000000f +/* Field instance: sdr::ctrlgrp::dramsts */ +#define SDR_CTRLGRP_DRAMSTS_DBEERR_MASK 0x00000008 +#define SDR_CTRLGRP_DRAMSTS_SBEERR_MASK 0x00000004 -/* function declaration */ -inline unsigned long sdram_calculate_size(void) { return 0; } -inline unsigned sdram_mmr_init_full(unsigned int sdr_phy_reg) { return 0; } -inline int sdram_calibration_full(void) { return 0; } +/* SDRAM width macro for configuration with ECC */ +#define SDRAM_WIDTH_32BIT_WITH_ECC 40 +#define SDRAM_WIDTH_16BIT_WITH_ECC 24 -#endif /* __ARCH_SDRAM_H__ */ +#endif +#endif /* _SDRAM_H_ */ diff --git a/arch/arm/mach-socfpga/include/mach/system_manager.h b/arch/arm/mach-socfpga/include/mach/system_manager.h index 51d98157783..46af30b640e 100644 --- a/arch/arm/mach-socfpga/include/mach/system_manager.h +++ b/arch/arm/mach-socfpga/include/mach/system_manager.h @@ -10,11 +10,10 @@ #ifndef __ASSEMBLY__ void sysmgr_pinmux_init(void); -void sysmgr_enable_warmrstcfgio(void); - -/* declaration for handoff table type */ -extern unsigned long sys_mgr_init_table[CONFIG_HPS_PINMUX_NUM]; +void sysmgr_config_warmrstcfgio(int enable); +void sysmgr_get_pinmux_table(const unsigned long **table, + unsigned int *table_len); #endif struct socfpga_system_manager { diff --git a/arch/arm/mach-socfpga/misc.c b/arch/arm/mach-socfpga/misc.c index 0f8b4d095d4..6128d54b188 100644 --- a/arch/arm/mach-socfpga/misc.c +++ b/arch/arm/mach-socfpga/misc.c @@ -6,17 +6,23 @@ #include <common.h> #include <asm/io.h> +#include <errno.h> +#include <fdtdec.h> +#include <libfdt.h> #include <altera.h> #include <miiphy.h> #include <netdev.h> #include <watchdog.h> #include <asm/arch/reset_manager.h> +#include <asm/arch/scan_manager.h> #include <asm/arch/system_manager.h> #include <asm/arch/dwmmc.h> #include <asm/arch/nic301.h> #include <asm/arch/scu.h> #include <asm/pl310.h> +#include <dt-bindings/reset/altr,rst-mgr.h> + DECLARE_GLOBAL_DATA_PTR; static struct pl310_regs *const pl310 = @@ -50,23 +56,20 @@ void enable_caches(void) * DesignWare Ethernet initialization */ #ifdef CONFIG_ETH_DESIGNWARE -int cpu_eth_init(bd_t *bis) +static void dwmac_deassert_reset(const unsigned int of_reset_id) { -#if CONFIG_EMAC_BASE == SOCFPGA_EMAC0_ADDRESS - const int physhift = SYSMGR_EMACGRP_CTRL_PHYSEL0_LSB; -#elif CONFIG_EMAC_BASE == SOCFPGA_EMAC1_ADDRESS - const int physhift = SYSMGR_EMACGRP_CTRL_PHYSEL1_LSB; -#else -#error "Incorrect CONFIG_EMAC_BASE value!" -#endif - - /* Initialize EMAC. This needs to be done at least once per boot. */ - - /* - * Putting the EMAC controller to reset when configuring the PHY - * interface select at System Manager - */ - socfpga_emac_reset(1); + u32 physhift, reset; + + if (of_reset_id == EMAC0_RESET) { + physhift = SYSMGR_EMACGRP_CTRL_PHYSEL0_LSB; + reset = SOCFPGA_RESET(EMAC0); + } else if (of_reset_id == EMAC1_RESET) { + physhift = SYSMGR_EMACGRP_CTRL_PHYSEL1_LSB; + reset = SOCFPGA_RESET(EMAC1); + } else { + printf("GMAC: Invalid reset ID (%i)!\n", of_reset_id); + return; + } /* Clearing emac0 PHY interface select to 0 */ clrbits_le32(&sysmgr_regs->emacgrp_ctrl, @@ -77,11 +80,41 @@ int cpu_eth_init(bd_t *bis) SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII << physhift); /* Release the EMAC controller from reset */ - socfpga_emac_reset(0); + socfpga_per_reset(reset, 0); +} + +int cpu_eth_init(bd_t *bis) +{ + const void *fdt = gd->fdt_blob; + struct fdtdec_phandle_args args; + int nodes[2]; /* Max. two GMACs */ + int ret, count; + int i, node; + + /* Put both GMACs into RESET state. */ + socfpga_per_reset(SOCFPGA_RESET(EMAC0), 1); + socfpga_per_reset(SOCFPGA_RESET(EMAC1), 1); + + count = fdtdec_find_aliases_for_id(fdt, "ethernet", + COMPAT_ALTERA_SOCFPGA_DWMAC, + nodes, ARRAY_SIZE(nodes)); + for (i = 0; i < count; i++) { + node = nodes[i]; + if (node <= 0) + continue; + + ret = fdtdec_parse_phandle_with_args(fdt, node, "resets", + "#reset-cells", 1, 0, + &args); + if (ret || (args.args_count != 1)) { + debug("GMAC%i: Failed to parse DT 'resets'!\n", i); + continue; + } + + dwmac_deassert_reset(args.args[0]); + } - /* initialize and register the emac */ - return designware_initialize(CONFIG_EMAC_BASE, - CONFIG_PHY_INTERFACE_MODE); + return 0; } #endif @@ -92,18 +125,110 @@ int cpu_eth_init(bd_t *bis) */ int cpu_mmc_init(bd_t *bis) { +/* + * FIXME: Temporarily define CONFIG_HPS_SDMMC_BUSWIDTH to prevent breakage + * due to missing patches in u-boot/master . The upcoming patch will + * switch this to OF probing, so this whole block will go away. + */ +#define CONFIG_HPS_SDMMC_BUSWIDTH 8 return socfpga_dwmmc_init(SOCFPGA_SDMMC_ADDRESS, CONFIG_HPS_SDMMC_BUSWIDTH, 0); } #endif -#if defined(CONFIG_DISPLAY_CPUINFO) +struct { + const char *mode; + const char *name; +} bsel_str[] = { + { "rsvd", "Reserved", }, + { "fpga", "FPGA (HPS2FPGA Bridge)", }, + { "nand", "NAND Flash (1.8V)", }, + { "nand", "NAND Flash (3.0V)", }, + { "sd", "SD/MMC External Transceiver (1.8V)", }, + { "sd", "SD/MMC Internal Transceiver (3.0V)", }, + { "qspi", "QSPI Flash (1.8V)", }, + { "qspi", "QSPI Flash (3.0V)", }, +}; + +static const struct { + const u16 pn; + const char *name; + const char *var; +} const socfpga_fpga_model[] = { + /* Cyclone V E */ + { 0x2b15, "Cyclone V, E/A2", "cv_e_a2" }, + { 0x2b05, "Cyclone V, E/A4", "cv_e_a4" }, + { 0x2b22, "Cyclone V, E/A5", "cv_e_a5" }, + { 0x2b13, "Cyclone V, E/A7", "cv_e_a7" }, + { 0x2b14, "Cyclone V, E/A9", "cv_e_a9" }, + /* Cyclone V GX/GT */ + { 0x2b01, "Cyclone V, GX/C3", "cv_gx_c3" }, + { 0x2b12, "Cyclone V, GX/C4", "cv_gx_c4" }, + { 0x2b02, "Cyclone V, GX/C5 or GT/D5", "cv_gx_c5" }, + { 0x2b03, "Cyclone V, GX/C7 or GT/D7", "cv_gx_c7" }, + { 0x2b04, "Cyclone V, GX/C9 or GT/D9", "cv_gx_c9" }, + /* Cyclone V SE/SX/ST */ + { 0x2d11, "Cyclone V, SE/A2 or SX/C2", "cv_se_a2" }, + { 0x2d01, "Cyclone V, SE/A4 or SX/C4", "cv_se_a4" }, + { 0x2d12, "Cyclone V, SE/A5 or SX/C5 or ST/D5", "cv_se_a5" }, + { 0x2d02, "Cyclone V, SE/A6 or SX/C6 or ST/D6", "cv_se_a6" }, + /* Arria V */ + { 0x2d03, "Arria V, D5", "av_d5" }, +}; + +static int socfpga_fpga_id(const bool print_id) +{ + const u32 altera_mi = 0x6e; + const u32 id = scan_mgr_get_fpga_id(); + + const u32 lsb = id & 0x00000001; + const u32 mi = (id >> 1) & 0x000007ff; + const u32 pn = (id >> 12) & 0x0000ffff; + const u32 version = (id >> 28) & 0x0000000f; + int i; + + if ((mi != altera_mi) || (lsb != 1)) { + printf("FPGA: Not Altera chip ID\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(socfpga_fpga_model); i++) + if (pn == socfpga_fpga_model[i].pn) + break; + + if (i == ARRAY_SIZE(socfpga_fpga_model)) { + printf("FPGA: Unknown Altera chip, ID 0x%08x\n", id); + return -EINVAL; + } + + if (print_id) + printf("FPGA: Altera %s, version 0x%01x\n", + socfpga_fpga_model[i].name, version); + return i; +} + /* * Print CPU information */ +#if defined(CONFIG_DISPLAY_CPUINFO) int print_cpuinfo(void) { + const u32 bsel = readl(&sysmgr_regs->bootinfo) & 0x7; puts("CPU: Altera SoCFPGA Platform\n"); + socfpga_fpga_id(1); + printf("BOOT: %s\n", bsel_str[bsel].name); + return 0; +} +#endif + +#ifdef CONFIG_ARCH_MISC_INIT +int arch_misc_init(void) +{ + const u32 bsel = readl(&sysmgr_regs->bootinfo) & 0x7; + const int fpga_id = socfpga_fpga_id(0); + setenv("bootmode", bsel_str[bsel].mode); + if (fpga_id >= 0) + setenv("fpgatype", socfpga_fpga_model[fpga_id].var); return 0; } #endif @@ -164,8 +289,10 @@ int arch_cpu_init(void) * If the HW watchdog is NOT enabled, make sure it is not running, * for example because it was enabled in the preloader. This might * trigger a watchdog-triggered reboot of Linux kernel later. + * Toggle watchdog reset, so watchdog in not running state. */ - socfpga_watchdog_reset(); + socfpga_per_reset(SOCFPGA_RESET(L4WD0), 1); + socfpga_per_reset(SOCFPGA_RESET(L4WD0), 0); #endif return 0; @@ -189,6 +316,16 @@ static uint32_t iswgrp_handoff[8]; int arch_early_init_r(void) { int i; + + /* + * Write magic value into magic register to unlock support for + * issuing warm reset. The ancient kernel code expects this + * value to be written into the register by the bootloader, so + * to support that old code, we write it here instead of in the + * reset_cpu() function just before reseting the CPU. + */ + writel(0xae9efebc, &sysmgr_regs->romcodegrp_warmramgrp_enable); + for (i = 0; i < 8; i++) /* Cache initial SW setting regs */ iswgrp_handoff[i] = readl(&sysmgr_regs->iswgrp_handoff[i]); @@ -215,7 +352,8 @@ int arch_early_init_r(void) #ifdef CONFIG_DESIGNWARE_SPI /* Get Designware SPI controller out of reset */ - socfpga_spim_enable(); + socfpga_per_reset(SOCFPGA_RESET(SPIM0), 0); + socfpga_per_reset(SOCFPGA_RESET(SPIM1), 0); #endif return 0; diff --git a/arch/arm/mach-socfpga/reset_manager.c b/arch/arm/mach-socfpga/reset_manager.c index 45b352bdfc0..1186358a71a 100644 --- a/arch/arm/mach-socfpga/reset_manager.c +++ b/arch/arm/mach-socfpga/reset_manager.c @@ -15,16 +15,41 @@ DECLARE_GLOBAL_DATA_PTR; static const struct socfpga_reset_manager *reset_manager_base = (void *)SOCFPGA_RSTMGR_ADDRESS; -/* Toggle reset signal to watchdog (WDT is disabled after this operation!) */ -void socfpga_watchdog_reset(void) +/* Assert or de-assert SoCFPGA reset manager reset. */ +void socfpga_per_reset(u32 reset, int set) { - /* assert reset for watchdog */ - setbits_le32(&reset_manager_base->per_mod_reset, - 1 << RSTMGR_PERMODRST_L4WD0_LSB); + const void *reg; + + if (RSTMGR_BANK(reset) == 0) + reg = &reset_manager_base->mpu_mod_reset; + else if (RSTMGR_BANK(reset) == 1) + reg = &reset_manager_base->per_mod_reset; + else if (RSTMGR_BANK(reset) == 2) + reg = &reset_manager_base->per2_mod_reset; + else if (RSTMGR_BANK(reset) == 3) + reg = &reset_manager_base->brg_mod_reset; + else if (RSTMGR_BANK(reset) == 4) + reg = &reset_manager_base->misc_mod_reset; + else /* Invalid reset register, do nothing */ + return; + + if (set) + setbits_le32(reg, 1 << RSTMGR_RESET(reset)); + else + clrbits_le32(reg, 1 << RSTMGR_RESET(reset)); +} + +/* + * Assert reset on every peripheral but L4WD0. + * Watchdog must be kept intact to prevent glitches + * and/or hangs. + */ +void socfpga_per_reset_all(void) +{ + const u32 l4wd0 = 1 << RSTMGR_RESET(SOCFPGA_RESET(L4WD0)); - /* deassert watchdog from reset (watchdog in not running state) */ - clrbits_le32(&reset_manager_base->per_mod_reset, - 1 << RSTMGR_PERMODRST_L4WD0_LSB); + writel(~l4wd0, &reset_manager_base->per_mod_reset); + writel(0xffffffff, &reset_manager_base->per2_mod_reset); } /* @@ -73,10 +98,10 @@ void socfpga_bridges_reset(int enable) writel(0xffffffff, &reset_manager_base->brg_mod_reset); } else { /* Check signal from FPGA. */ - if (fpgamgr_poll_fpga_ready()) { - /* FPGA not ready. Wait for watchdog timeout. */ - printf("%s: fpga not ready, hanging.\n", __func__); - hang(); + if (!fpgamgr_test_fpga_ready()) { + /* FPGA not ready, do nothing. */ + printf("%s: FPGA not ready, aborting.\n", __func__); + return; } /* brdmodrst */ @@ -87,53 +112,3 @@ void socfpga_bridges_reset(int enable) } } #endif - -/* Change the reset state for EMAC 0 and EMAC 1 */ -void socfpga_emac_reset(int enable) -{ - const void *reset = &reset_manager_base->per_mod_reset; - - if (enable) { - setbits_le32(reset, 1 << RSTMGR_PERMODRST_EMAC0_LSB); - setbits_le32(reset, 1 << RSTMGR_PERMODRST_EMAC1_LSB); - } else { -#if (CONFIG_EMAC_BASE == SOCFPGA_EMAC0_ADDRESS) - clrbits_le32(reset, 1 << RSTMGR_PERMODRST_EMAC0_LSB); -#elif (CONFIG_EMAC_BASE == SOCFPGA_EMAC1_ADDRESS) - clrbits_le32(reset, 1 << RSTMGR_PERMODRST_EMAC1_LSB); -#endif - } -} - -/* SPI Master enable (its held in reset by the preloader) */ -void socfpga_spim_enable(void) -{ - const void *reset = &reset_manager_base->per_mod_reset; - - clrbits_le32(reset, (1 << RSTMGR_PERMODRST_SPIM0_LSB) | - (1 << RSTMGR_PERMODRST_SPIM1_LSB)); -} - -/* Bring UART0 out of reset. */ -void socfpga_uart0_enable(void) -{ - const void *reset = &reset_manager_base->per_mod_reset; - - clrbits_le32(reset, 1 << RSTMGR_PERMODRST_UART0_LSB); -} - -/* Bring SDRAM controller out of reset. */ -void socfpga_sdram_enable(void) -{ - const void *reset = &reset_manager_base->per_mod_reset; - - clrbits_le32(reset, 1 << RSTMGR_PERMODRST_SDR_LSB); -} - -/* Bring OSC1 timer out of reset. */ -void socfpga_osc1timer_enable(void) -{ - const void *reset = &reset_manager_base->per_mod_reset; - - clrbits_le32(reset, 1 << RSTMGR_PERMODRST_OSC1TIMER0_LSB); -} diff --git a/arch/arm/mach-socfpga/scan_manager.c b/arch/arm/mach-socfpga/scan_manager.c index a820b1b1bfc..566b33f2b6a 100644 --- a/arch/arm/mach-socfpga/scan_manager.c +++ b/arch/arm/mach-socfpga/scan_manager.c @@ -5,9 +5,28 @@ */ #include <common.h> +#include <errno.h> #include <asm/io.h> #include <asm/arch/freeze_controller.h> #include <asm/arch/scan_manager.h> +#include <asm/arch/system_manager.h> + +/* + * Maximum polling loop to wait for IO scan chain engine becomes idle + * to prevent infinite loop. It is important that this is NOT changed + * to delay using timer functions, since at the time this function is + * called, timer might not yet be inited. + */ +#define SCANMGR_MAX_DELAY 100 + +/* + * Maximum length of TDI_TDO packet payload is 128 bits, + * represented by (length - 1) in TDI_TDO header. + */ +#define TDI_TDO_MAX_PAYLOAD 127 + +#define SCANMGR_STAT_ACTIVE (1 << 31) +#define SCANMGR_STAT_WFIFOCNT_MASK 0x70000000 DECLARE_GLOBAL_DATA_PTR; @@ -15,41 +34,122 @@ static const struct socfpga_scan_manager *scan_manager_base = (void *)(SOCFPGA_SCANMGR_ADDRESS); static const struct socfpga_freeze_controller *freeze_controller_base = (void *)(SOCFPGA_SYSMGR_ADDRESS + SYSMGR_FRZCTRL_ADDRESS); +static struct socfpga_system_manager *sys_mgr_base = + (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; -/* +/** + * scan_chain_engine_is_idle() - Check if the JTAG scan chain is idle + * @max_iter: Maximum number of iterations to wait for idle + * * Function to check IO scan chain engine status and wait if the engine is * is active. Poll the IO scan chain engine till maximum iteration reached. */ -static inline uint32_t scan_chain_engine_is_idle(uint32_t max_iter) +static u32 scan_chain_engine_is_idle(u32 max_iter) { - uint32_t scanmgr_status; - - scanmgr_status = readl(&scan_manager_base->stat); + const u32 mask = SCANMGR_STAT_ACTIVE | SCANMGR_STAT_WFIFOCNT_MASK; + u32 status; - /* Poll the engine until the scan engine is inactive */ - while (SCANMGR_STAT_ACTIVE_GET(scanmgr_status) || - (SCANMGR_STAT_WFIFOCNT_GET(scanmgr_status) > 0)) { - max_iter--; - if (max_iter > 0) - scanmgr_status = readl(&scan_manager_base->stat); - else + /* Poll the engine until the scan engine is inactive. */ + do { + status = readl(&scan_manager_base->stat); + if (!(status & mask)) return 0; + } while (max_iter--); + + return -ETIMEDOUT; +} + +#define JTAG_BP_INSN (1 << 0) +#define JTAG_BP_TMS (1 << 1) +#define JTAG_BP_PAYLOAD (1 << 2) +#define JTAG_BP_2BYTE (1 << 3) +#define JTAG_BP_4BYTE (1 << 4) + +/** + * scan_mgr_jtag_io() - Access the JTAG chain + * @flags: Control flags, used to configure the action on the JTAG + * @iarg: Instruction argument + * @parg: Payload argument or data + * + * Perform I/O on the JTAG chain + */ +static void scan_mgr_jtag_io(const u32 flags, const u8 iarg, const u32 parg) +{ + u32 data = parg; + + if (flags & JTAG_BP_INSN) { /* JTAG instruction */ + /* + * The SCC JTAG register is LSB first, so make + * space for the instruction at the LSB. + */ + data <<= 8; + if (flags & JTAG_BP_TMS) { + data |= (0 << 7); /* TMS instruction. */ + data |= iarg & 0x3f; /* TMS arg is 6 bits. */ + if (flags & JTAG_BP_PAYLOAD) + data |= (1 << 6); + } else { + data |= (1 << 7); /* TDI/TDO instruction. */ + data |= iarg & 0xf; /* TDI/TDO arg is 4 bits. */ + if (flags & JTAG_BP_PAYLOAD) + data |= (1 << 4); + } + } + + if (flags & JTAG_BP_4BYTE) + writel(data, &scan_manager_base->fifo_quad_byte); + else if (flags & JTAG_BP_2BYTE) + writel(data & 0xffff, &scan_manager_base->fifo_double_byte); + else + writel(data & 0xff, &scan_manager_base->fifo_single_byte); +} + +/** + * scan_mgr_jtag_insn_data() - Send JTAG instruction and data + * @iarg: Instruction argument + * @data: Associated data + * @dlen: Length of data in bits + * + * This function is used when programming the IO chains to submit the + * instruction followed by variable length payload. + */ +static int +scan_mgr_jtag_insn_data(const u8 iarg, const unsigned long *data, + const unsigned int dlen) +{ + int i, j; + + scan_mgr_jtag_io(JTAG_BP_INSN | JTAG_BP_2BYTE, iarg, dlen - 1); + + /* 32 bits or more remain */ + for (i = 0; i < dlen / 32; i++) + scan_mgr_jtag_io(JTAG_BP_4BYTE, 0x0, data[i]); + + if ((dlen % 32) > 24) { /* 31...24 bits remain */ + scan_mgr_jtag_io(JTAG_BP_4BYTE, 0x0, data[i]); + } else if (dlen % 32) { /* 24...1 bit remain */ + for (j = 0; j < dlen % 32; j += 8) + scan_mgr_jtag_io(0, 0x0, data[i] >> j); } - return 1; + + return scan_chain_engine_is_idle(SCANMGR_MAX_DELAY); } -/* Program HPS IO Scan Chain */ -uint32_t scan_mgr_io_scan_chain_prg( - uint32_t io_scan_chain_id, - uint32_t io_scan_chain_len_in_bits, - const uint32_t *iocsr_scan_chain) +/** + * scan_mgr_io_scan_chain_prg() - Program HPS IO Scan Chain + * @io_scan_chain_id: IO scan chain ID + */ +static int scan_mgr_io_scan_chain_prg(const unsigned int io_scan_chain_id) { - uint16_t tdi_tdo_header; - uint32_t io_program_iter; - uint32_t io_scan_chain_data_residual; - uint32_t residual; - uint32_t i; - uint32_t index = 0; + u32 io_scan_chain_len_in_bits; + const unsigned long *iocsr_scan_chain; + unsigned int rem, idx = 0; + int ret; + + ret = iocsr_get_config_table(io_scan_chain_id, &iocsr_scan_chain, + &io_scan_chain_len_in_bits); + if (ret) + return 1; /* * De-assert reinit if the IO scan chain is intended for HIO. In @@ -63,8 +163,9 @@ uint32_t scan_mgr_io_scan_chain_prg( * Check if the scan chain engine is inactive and the * WFIFO is empty before enabling the IO scan chain */ - if (!scan_chain_engine_is_idle(SCAN_MAX_DELAY)) - return 1; + ret = scan_chain_engine_is_idle(SCANMGR_MAX_DELAY); + if (ret) + return ret; /* * Enable IO Scan chain based on scan chain id @@ -72,114 +173,18 @@ uint32_t scan_mgr_io_scan_chain_prg( */ setbits_le32(&scan_manager_base->en, 1 << io_scan_chain_id); - /* - * Calculate number of iteration needed for full 128-bit (4 x32-bits) - * bits shifting. Each TDI_TDO packet can shift in maximum 128-bits - */ - io_program_iter = io_scan_chain_len_in_bits >> - IO_SCAN_CHAIN_128BIT_SHIFT; - io_scan_chain_data_residual = io_scan_chain_len_in_bits & - IO_SCAN_CHAIN_128BIT_MASK; - - /* Construct TDI_TDO packet for 128-bit IO scan chain (2 bytes) */ - tdi_tdo_header = TDI_TDO_HEADER_FIRST_BYTE | - (TDI_TDO_MAX_PAYLOAD << TDI_TDO_HEADER_SECOND_BYTE_SHIFT); - - /* Program IO scan chain in 128-bit iteration */ - for (i = 0; i < io_program_iter; i++) { - /* write TDI_TDO packet header to scan manager */ - writel(tdi_tdo_header, &scan_manager_base->fifo_double_byte); - - /* calculate array index. Multiply by 4 as write 4 x 32bits */ - index = i * 4; - - /* write 4 successive 32-bit IO scan chain data into WFIFO */ - writel(iocsr_scan_chain[index], - &scan_manager_base->fifo_quad_byte); - writel(iocsr_scan_chain[index + 1], - &scan_manager_base->fifo_quad_byte); - writel(iocsr_scan_chain[index + 2], - &scan_manager_base->fifo_quad_byte); - writel(iocsr_scan_chain[index + 3], - &scan_manager_base->fifo_quad_byte); - - /* - * Check if the scan chain engine has completed the - * IO scan chain data shifting - */ - if (!scan_chain_engine_is_idle(SCAN_MAX_DELAY)) - goto error; - } - - /* Calculate array index for final TDI_TDO packet */ - index = io_program_iter * 4; - - /* Final TDI_TDO packet if any */ - if (io_scan_chain_data_residual) { - /* - * Calculate number of quad bytes FIFO write - * needed for the final TDI_TDO packet - */ - io_program_iter = io_scan_chain_data_residual >> - IO_SCAN_CHAIN_32BIT_SHIFT; - - /* - * Construct TDI_TDO packet for remaining IO - * scan chain (2 bytes) - */ - tdi_tdo_header = TDI_TDO_HEADER_FIRST_BYTE | - ((io_scan_chain_data_residual - 1) << - TDI_TDO_HEADER_SECOND_BYTE_SHIFT); - - /* - * Program the last part of IO scan chain write TDI_TDO packet - * header (2 bytes) to scan manager - */ - writel(tdi_tdo_header, &scan_manager_base->fifo_double_byte); - - for (i = 0; i < io_program_iter; i++) { - /* - * write remaining scan chain data into scan - * manager WFIFO with 4 bytes write - */ - writel(iocsr_scan_chain[index + i], - &scan_manager_base->fifo_quad_byte); - } - - index += io_program_iter; - residual = io_scan_chain_data_residual & - IO_SCAN_CHAIN_32BIT_MASK; - - if (IO_SCAN_CHAIN_PAYLOAD_24BIT < residual) { - /* - * write the last 4B scan chain data - * into scan manager WFIFO - */ - writel(iocsr_scan_chain[index], - &scan_manager_base->fifo_quad_byte); - } else { - /* - * write the remaining 1 - 3 bytes scan chain - * data into scan manager WFIFO byte by byte - * to prevent JTAG engine shifting unused data - * from the FIFO and mistaken the data as a - * valid command (even though unused bits are - * set to 0, but just to prevent hardware - * glitch) - */ - for (i = 0; i < residual; i += 8) { - writel(((iocsr_scan_chain[index] >> i) - & IO_SCAN_CHAIN_BYTE_MASK), - &scan_manager_base->fifo_single_byte); - } - } + /* Program IO scan chain. */ + while (io_scan_chain_len_in_bits) { + if (io_scan_chain_len_in_bits > 128) + rem = 128; + else + rem = io_scan_chain_len_in_bits; - /* - * Check if the scan chain engine has completed the - * IO scan chain data shifting - */ - if (!scan_chain_engine_is_idle(SCAN_MAX_DELAY)) + ret = scan_mgr_jtag_insn_data(0x0, &iocsr_scan_chain[idx], rem); + if (ret) goto error; + io_scan_chain_len_in_bits -= rem; + idx += 4; } /* Disable IO Scan chain when configuration done*/ @@ -189,7 +194,7 @@ uint32_t scan_mgr_io_scan_chain_prg( error: /* Disable IO Scan chain when error detected */ clrbits_le32(&scan_manager_base->en, 1 << io_scan_chain_id); - return 1; + return ret; } int scan_mgr_configure_iocsr(void) @@ -197,13 +202,61 @@ int scan_mgr_configure_iocsr(void) int status = 0; /* configure the IOCSR through scan chain */ - status |= scan_mgr_io_scan_chain_prg(0, - CONFIG_HPS_IOCSR_SCANCHAIN0_LENGTH, iocsr_scan_chain0_table); - status |= scan_mgr_io_scan_chain_prg(1, - CONFIG_HPS_IOCSR_SCANCHAIN1_LENGTH, iocsr_scan_chain1_table); - status |= scan_mgr_io_scan_chain_prg(2, - CONFIG_HPS_IOCSR_SCANCHAIN2_LENGTH, iocsr_scan_chain2_table); - status |= scan_mgr_io_scan_chain_prg(3, - CONFIG_HPS_IOCSR_SCANCHAIN3_LENGTH, iocsr_scan_chain3_table); + status |= scan_mgr_io_scan_chain_prg(0); + status |= scan_mgr_io_scan_chain_prg(1); + status |= scan_mgr_io_scan_chain_prg(2); + status |= scan_mgr_io_scan_chain_prg(3); return status; } + +/** + * scan_mgr_get_fpga_id() - Obtain FPGA JTAG ID + * + * This function obtains JTAG ID from the FPGA TAP controller. + */ +u32 scan_mgr_get_fpga_id(void) +{ + const unsigned long data = 0; + u32 id = 0xffffffff; + int ret; + + /* Enable HPS to talk to JTAG in the FPGA through the System Manager */ + writel(0x1, &sys_mgr_base->scanmgrgrp_ctrl); + + /* Enable port 7 */ + writel(0x80, &scan_manager_base->en); + /* write to CSW to make s2f_ntrst reset */ + writel(0x02, &scan_manager_base->stat); + + /* Add a pause */ + mdelay(1); + + /* write 0x00 to CSW to clear the s2f_ntrst */ + writel(0, &scan_manager_base->stat); + + /* + * Go to Test-Logic-Reset state. + * This sets TAP controller into IDCODE mode. + */ + scan_mgr_jtag_io(JTAG_BP_INSN | JTAG_BP_TMS, 0x1f | (1 << 5), 0x0); + + /* Go to Run-Test/Idle -> DR-Scan -> Capture-DR -> Shift-DR state. */ + scan_mgr_jtag_io(JTAG_BP_INSN | JTAG_BP_TMS, 0x02 | (1 << 4), 0x0); + + /* + * Push 4 bytes of data through TDI->DR->TDO. + * + * Length of TDI data is 32bits (length - 1) and they are only + * zeroes as we care only for TDO data. + */ + ret = scan_mgr_jtag_insn_data(0x4, &data, 32); + /* Read 32 bit from captured JTAG data. */ + if (!ret) + id = readl(&scan_manager_base->fifo_quad_byte); + + /* Disable all port */ + writel(0, &scan_manager_base->en); + writel(0, &sys_mgr_base->scanmgrgrp_ctrl); + + return id; +} diff --git a/arch/arm/mach-socfpga/spl.c b/arch/arm/mach-socfpga/spl.c index f9946584521..13ec24bc169 100644 --- a/arch/arm/mach-socfpga/spl.c +++ b/arch/arm/mach-socfpga/spl.c @@ -17,42 +17,74 @@ #include <asm/arch/clock_manager.h> #include <asm/arch/scan_manager.h> #include <asm/arch/sdram.h> +#include <asm/arch/scu.h> +#include <asm/arch/nic301.h> DECLARE_GLOBAL_DATA_PTR; static struct pl310_regs *const pl310 = (struct pl310_regs *)CONFIG_SYS_PL310_BASE; +static struct scu_registers *scu_regs = + (struct scu_registers *)SOCFPGA_MPUSCU_ADDRESS; +static struct nic301_registers *nic301_regs = + (struct nic301_registers *)SOCFPGA_L3REGS_ADDRESS; +static struct socfpga_system_manager *sysmgr_regs = + (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; -#define MAIN_VCO_BASE ( \ - (CONFIG_HPS_MAINPLLGRP_VCO_DENOM << \ - CLKMGR_MAINPLLGRP_VCO_DENOM_OFFSET) | \ - (CONFIG_HPS_MAINPLLGRP_VCO_NUMER << \ - CLKMGR_MAINPLLGRP_VCO_NUMER_OFFSET) \ - ) - -#define PERI_VCO_BASE ( \ - (CONFIG_HPS_PERPLLGRP_VCO_PSRC << \ - CLKMGR_PERPLLGRP_VCO_PSRC_OFFSET) | \ - (CONFIG_HPS_PERPLLGRP_VCO_DENOM << \ - CLKMGR_PERPLLGRP_VCO_DENOM_OFFSET) | \ - (CONFIG_HPS_PERPLLGRP_VCO_NUMER << \ - CLKMGR_PERPLLGRP_VCO_NUMER_OFFSET) \ - ) - -#define SDR_VCO_BASE ( \ - (CONFIG_HPS_SDRPLLGRP_VCO_SSRC << \ - CLKMGR_SDRPLLGRP_VCO_SSRC_OFFSET) | \ - (CONFIG_HPS_SDRPLLGRP_VCO_DENOM << \ - CLKMGR_SDRPLLGRP_VCO_DENOM_OFFSET) | \ - (CONFIG_HPS_SDRPLLGRP_VCO_NUMER << \ - CLKMGR_SDRPLLGRP_VCO_NUMER_OFFSET) \ - ) +u32 spl_boot_device(void) +{ + const u32 bsel = readl(&sysmgr_regs->bootinfo); + + switch (bsel & 0x7) { + case 0x1: /* FPGA (HPS2FPGA Bridge) */ + return BOOT_DEVICE_RAM; + case 0x2: /* NAND Flash (1.8V) */ + case 0x3: /* NAND Flash (3.0V) */ + return BOOT_DEVICE_NAND; + case 0x4: /* SD/MMC External Transceiver (1.8V) */ + case 0x5: /* SD/MMC Internal Transceiver (3.0V) */ + socfpga_per_reset(SOCFPGA_RESET(SDMMC), 0); + socfpga_per_reset(SOCFPGA_RESET(DMA), 0); + return BOOT_DEVICE_MMC1; + case 0x6: /* QSPI Flash (1.8V) */ + case 0x7: /* QSPI Flash (3.0V) */ + socfpga_per_reset(SOCFPGA_RESET(QSPI), 0); + return BOOT_DEVICE_SPI; + default: + printf("Invalid boot device (bsel=%08x)!\n", bsel); + hang(); + } +} + +#ifdef CONFIG_SPL_MMC_SUPPORT +u32 spl_boot_mode(void) +{ +#if defined(CONFIG_SPL_FAT_SUPPORT) || defined(CONFIG_SPL_EXT_SUPPORT) + return MMCSD_MODE_FS; +#else + return MMCSD_MODE_RAW; +#endif +} +#endif + +static void socfpga_nic301_slave_ns(void) +{ + writel(0x1, &nic301_regs->lwhps2fpgaregs); + writel(0x1, &nic301_regs->hps2fpgaregs); + writel(0x1, &nic301_regs->acp); + writel(0x1, &nic301_regs->rom); + writel(0x1, &nic301_regs->ocram); + writel(0x1, &nic301_regs->sdrdata); +} void board_init_f(ulong dummy) { - struct socfpga_system_manager *sysmgr_regs = - (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; +#ifndef CONFIG_SOCFPGA_VIRTUAL_TARGET + const struct cm_config *cm_default_cfg = cm_get_default_config(); +#endif + unsigned long sdram_size; unsigned long reg; + /* * First C code to run. Clear fake OCRAM ECC first as SBE * and DBE might triggered during power on @@ -67,137 +99,54 @@ void board_init_f(ulong dummy) memset(__bss_start, 0, __bss_end - __bss_start); - /* Remap SDRAM to 0x0 */ - writel(0x1, &pl310->pl310_addr_filter_start); + socfpga_nic301_slave_ns(); - board_init_r(NULL, 0); -} + /* Configure ARM MPU SNSAC register. */ + setbits_le32(&scu_regs->sacr, 0xfff); -u32 spl_boot_device(void) -{ - return BOOT_DEVICE_RAM; -} + /* Remap SDRAM to 0x0 */ + writel(0x1, &nic301_regs->remap); /* remap.mpuzero */ + writel(0x1, &pl310->pl310_addr_filter_start); -/* - * Board initialization after bss clearance - */ -void spl_board_init(void) -{ - unsigned long sdram_size; #ifndef CONFIG_SOCFPGA_VIRTUAL_TARGET - cm_config_t cm_default_cfg = { - /* main group */ - MAIN_VCO_BASE, - (CONFIG_HPS_MAINPLLGRP_MPUCLK_CNT << - CLKMGR_MAINPLLGRP_MPUCLK_CNT_OFFSET), - (CONFIG_HPS_MAINPLLGRP_MAINCLK_CNT << - CLKMGR_MAINPLLGRP_MAINCLK_CNT_OFFSET), - (CONFIG_HPS_MAINPLLGRP_DBGATCLK_CNT << - CLKMGR_MAINPLLGRP_DBGATCLK_CNT_OFFSET), - (CONFIG_HPS_MAINPLLGRP_MAINQSPICLK_CNT << - CLKMGR_MAINPLLGRP_MAINQSPICLK_CNT_OFFSET), - (CONFIG_HPS_MAINPLLGRP_MAINNANDSDMMCCLK_CNT << - CLKMGR_PERPLLGRP_PERNANDSDMMCCLK_CNT_OFFSET), - (CONFIG_HPS_MAINPLLGRP_CFGS2FUSER0CLK_CNT << - CLKMGR_MAINPLLGRP_CFGS2FUSER0CLK_CNT_OFFSET), - (CONFIG_HPS_MAINPLLGRP_MAINDIV_L3MPCLK << - CLKMGR_MAINPLLGRP_MAINDIV_L3MPCLK_OFFSET) | - (CONFIG_HPS_MAINPLLGRP_MAINDIV_L3SPCLK << - CLKMGR_MAINPLLGRP_MAINDIV_L3SPCLK_OFFSET) | - (CONFIG_HPS_MAINPLLGRP_MAINDIV_L4MPCLK << - CLKMGR_MAINPLLGRP_MAINDIV_L4MPCLK_OFFSET) | - (CONFIG_HPS_MAINPLLGRP_MAINDIV_L4SPCLK << - CLKMGR_MAINPLLGRP_MAINDIV_L4SPCLK_OFFSET), - (CONFIG_HPS_MAINPLLGRP_DBGDIV_DBGATCLK << - CLKMGR_MAINPLLGRP_DBGDIV_DBGATCLK_OFFSET) | - (CONFIG_HPS_MAINPLLGRP_DBGDIV_DBGCLK << - CLKMGR_MAINPLLGRP_DBGDIV_DBGCLK_OFFSET), - (CONFIG_HPS_MAINPLLGRP_TRACEDIV_TRACECLK << - CLKMGR_MAINPLLGRP_TRACEDIV_TRACECLK_OFFSET), - (CONFIG_HPS_MAINPLLGRP_L4SRC_L4MP << - CLKMGR_MAINPLLGRP_L4SRC_L4MP_OFFSET) | - (CONFIG_HPS_MAINPLLGRP_L4SRC_L4SP << - CLKMGR_MAINPLLGRP_L4SRC_L4SP_OFFSET), - - /* peripheral group */ - PERI_VCO_BASE, - (CONFIG_HPS_PERPLLGRP_EMAC0CLK_CNT << - CLKMGR_PERPLLGRP_EMAC0CLK_CNT_OFFSET), - (CONFIG_HPS_PERPLLGRP_EMAC1CLK_CNT << - CLKMGR_PERPLLGRP_EMAC1CLK_CNT_OFFSET), - (CONFIG_HPS_PERPLLGRP_PERQSPICLK_CNT << - CLKMGR_PERPLLGRP_PERQSPICLK_CNT_OFFSET), - (CONFIG_HPS_PERPLLGRP_PERNANDSDMMCCLK_CNT << - CLKMGR_PERPLLGRP_PERNANDSDMMCCLK_CNT_OFFSET), - (CONFIG_HPS_PERPLLGRP_PERBASECLK_CNT << - CLKMGR_PERPLLGRP_PERBASECLK_CNT_OFFSET), - (CONFIG_HPS_PERPLLGRP_S2FUSER1CLK_CNT << - CLKMGR_PERPLLGRP_S2FUSER1CLK_CNT_OFFSET), - (CONFIG_HPS_PERPLLGRP_DIV_USBCLK << - CLKMGR_PERPLLGRP_DIV_USBCLK_OFFSET) | - (CONFIG_HPS_PERPLLGRP_DIV_SPIMCLK << - CLKMGR_PERPLLGRP_DIV_SPIMCLK_OFFSET) | - (CONFIG_HPS_PERPLLGRP_DIV_CAN0CLK << - CLKMGR_PERPLLGRP_DIV_CAN0CLK_OFFSET) | - (CONFIG_HPS_PERPLLGRP_DIV_CAN1CLK << - CLKMGR_PERPLLGRP_DIV_CAN1CLK_OFFSET), - (CONFIG_HPS_PERPLLGRP_GPIODIV_GPIODBCLK << - CLKMGR_PERPLLGRP_GPIODIV_GPIODBCLK_OFFSET), - (CONFIG_HPS_PERPLLGRP_SRC_QSPI << - CLKMGR_PERPLLGRP_SRC_QSPI_OFFSET) | - (CONFIG_HPS_PERPLLGRP_SRC_NAND << - CLKMGR_PERPLLGRP_SRC_NAND_OFFSET) | - (CONFIG_HPS_PERPLLGRP_SRC_SDMMC << - CLKMGR_PERPLLGRP_SRC_SDMMC_OFFSET), - - /* sdram pll group */ - SDR_VCO_BASE, - (CONFIG_HPS_SDRPLLGRP_DDRDQSCLK_PHASE << - CLKMGR_SDRPLLGRP_DDRDQSCLK_PHASE_OFFSET) | - (CONFIG_HPS_SDRPLLGRP_DDRDQSCLK_CNT << - CLKMGR_SDRPLLGRP_DDRDQSCLK_CNT_OFFSET), - (CONFIG_HPS_SDRPLLGRP_DDR2XDQSCLK_PHASE << - CLKMGR_SDRPLLGRP_DDR2XDQSCLK_PHASE_OFFSET) | - (CONFIG_HPS_SDRPLLGRP_DDR2XDQSCLK_CNT << - CLKMGR_SDRPLLGRP_DDR2XDQSCLK_CNT_OFFSET), - (CONFIG_HPS_SDRPLLGRP_DDRDQCLK_PHASE << - CLKMGR_SDRPLLGRP_DDRDQCLK_PHASE_OFFSET) | - (CONFIG_HPS_SDRPLLGRP_DDRDQCLK_CNT << - CLKMGR_SDRPLLGRP_DDRDQCLK_CNT_OFFSET), - (CONFIG_HPS_SDRPLLGRP_S2FUSER2CLK_PHASE << - CLKMGR_SDRPLLGRP_S2FUSER2CLK_PHASE_OFFSET) | - (CONFIG_HPS_SDRPLLGRP_S2FUSER2CLK_CNT << - CLKMGR_SDRPLLGRP_S2FUSER2CLK_CNT_OFFSET), - - }; - debug("Freezing all I/O banks\n"); /* freeze all IO banks */ sys_mgr_frzctrl_freeze_req(); - socfpga_sdram_enable(); - socfpga_uart0_enable(); - socfpga_osc1timer_enable(); + /* Put everything into reset but L4WD0. */ + socfpga_per_reset_all(); + /* Put FPGA bridges into reset too. */ + socfpga_bridges_reset(1); + + socfpga_per_reset(SOCFPGA_RESET(SDR), 0); + socfpga_per_reset(SOCFPGA_RESET(UART0), 0); + socfpga_per_reset(SOCFPGA_RESET(OSC1TIMER0), 0); timer_init(); debug("Reconfigure Clock Manager\n"); /* reconfigure the PLLs */ - cm_basic_init(&cm_default_cfg); + cm_basic_init(cm_default_cfg); /* Enable bootrom to configure IOs. */ - sysmgr_enable_warmrstcfgio(); + sysmgr_config_warmrstcfgio(1); /* configure the IOCSR / IO buffer settings */ if (scan_mgr_configure_iocsr()) hang(); + sysmgr_config_warmrstcfgio(0); + /* configure the pin muxing through system manager */ + sysmgr_config_warmrstcfgio(1); sysmgr_pinmux_init(); + sysmgr_config_warmrstcfgio(0); + #endif /* CONFIG_SOCFPGA_VIRTUAL_TARGET */ - /* de-assert reset for peripherals and bridges based on handoff */ + /* De-assert reset for peripherals and bridges based on handoff */ reset_deassert_peripherals_handoff(); + socfpga_bridges_reset(0); debug("Unfreezing/Thaw all I/O banks\n"); /* unfreeze / thaw all IO banks */ @@ -226,4 +175,11 @@ void spl_board_init(void) puts("SDRAM size check failed!\n"); hang(); } + + socfpga_bridges_reset(1); + + /* Configure simple malloc base pointer into RAM. */ + gd->malloc_base = CONFIG_SYS_TEXT_BASE + (1024 * 1024); + + board_init_r(NULL, 0); } diff --git a/arch/arm/mach-socfpga/system_manager.c b/arch/arm/mach-socfpga/system_manager.c index 8126e0d43cf..744ec326b49 100644 --- a/arch/arm/mach-socfpga/system_manager.c +++ b/arch/arm/mach-socfpga/system_manager.c @@ -57,9 +57,13 @@ static void populate_sysmgr_fpgaintf_module(void) void sysmgr_pinmux_init(void) { uint32_t regs = (uint32_t)&sysmgr_regs->emacio[0]; + const unsigned long *sys_mgr_init_table; + unsigned int len; int i; - for (i = 0; i < ARRAY_SIZE(sys_mgr_init_table); i++) { + sysmgr_get_pinmux_table(&sys_mgr_init_table, &len); + + for (i = 0; i < len; i++) { writel(sys_mgr_init_table[i], regs); regs += sizeof(regs); } @@ -70,8 +74,12 @@ void sysmgr_pinmux_init(void) /* * This bit allows the bootrom to configure the IOs after a warm reset. */ -void sysmgr_enable_warmrstcfgio(void) +void sysmgr_config_warmrstcfgio(int enable) { - setbits_le32(&sysmgr_regs->romcodegrp_ctrl, - SYSMGR_ROMCODEGRP_CTRL_WARMRSTCFGIO); + if (enable) + setbits_le32(&sysmgr_regs->romcodegrp_ctrl, + SYSMGR_ROMCODEGRP_CTRL_WARMRSTCFGIO); + else + clrbits_le32(&sysmgr_regs->romcodegrp_ctrl, + SYSMGR_ROMCODEGRP_CTRL_WARMRSTCFGIO); } diff --git a/arch/arm/mach-socfpga/u-boot-spl.lds b/arch/arm/mach-socfpga/u-boot-spl.lds deleted file mode 100644 index 569fa418f46..00000000000 --- a/arch/arm/mach-socfpga/u-boot-spl.lds +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2012 Altera Corporation <www.altera.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -MEMORY { .sdram : ORIGIN = (0), LENGTH = (0xffffffff) } - -OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") -OUTPUT_ARCH(arm) -ENTRY(_start) -SECTIONS -{ - . = 0x00000000; - - . = ALIGN(4); - .text : - { - *(.vectors) - arch/arm/cpu/armv7/start.o (.text*) - *(.text*) - } >.sdram - - . = ALIGN(4); - .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } >.sdram - - . = ALIGN(4); - .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sdram - - . = ALIGN(4); - __image_copy_end = .; - - .end : - { - *(.__end) - } - - .bss : { - . = ALIGN(4); - __bss_start = .; - *(.bss*) - . = ALIGN(4); - __bss_end = .; - } >.sdram -} diff --git a/board/altera/socfpga/Makefile b/board/altera/socfpga/Makefile index c867f73ff7e..5a15c716106 100644 --- a/board/altera/socfpga/Makefile +++ b/board/altera/socfpga/Makefile @@ -6,5 +6,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y := socfpga.o -obj-$(CONFIG_SPL_BUILD) += pinmux_config.o iocsr_config.o +obj-y := socfpga.o wrap_pll_config.o +obj-$(CONFIG_SPL_BUILD) += wrap_iocsr_config.o wrap_pinmux_config.o \ + wrap_sdram_config.o diff --git a/board/altera/socfpga/iocsr_config.c b/board/altera/socfpga/qts/iocsr_config.c index 3b202b5b687..3b202b5b687 100644 --- a/board/altera/socfpga/iocsr_config.c +++ b/board/altera/socfpga/qts/iocsr_config.c diff --git a/board/altera/socfpga/iocsr_config.h b/board/altera/socfpga/qts/iocsr_config.h index d1c9b0d36a0..d1c9b0d36a0 100644 --- a/board/altera/socfpga/iocsr_config.h +++ b/board/altera/socfpga/qts/iocsr_config.h diff --git a/board/altera/socfpga/pinmux_config.c b/board/altera/socfpga/qts/pinmux_config.c index 7e7a18484fd..7e7a18484fd 100644 --- a/board/altera/socfpga/pinmux_config.c +++ b/board/altera/socfpga/qts/pinmux_config.c diff --git a/board/altera/socfpga/pinmux_config.h b/board/altera/socfpga/qts/pinmux_config.h index 21fabb0b2b4..21fabb0b2b4 100644 --- a/board/altera/socfpga/pinmux_config.h +++ b/board/altera/socfpga/qts/pinmux_config.h diff --git a/board/altera/socfpga/pll_config.h b/board/altera/socfpga/qts/pll_config.h index 7cd25df6145..7cd25df6145 100644 --- a/board/altera/socfpga/pll_config.h +++ b/board/altera/socfpga/qts/pll_config.h diff --git a/board/altera/socfpga/qts/sdram_config.h b/board/altera/socfpga/qts/sdram_config.h new file mode 100644 index 00000000000..f6d51ca8efb --- /dev/null +++ b/board/altera/socfpga/qts/sdram_config.h @@ -0,0 +1,100 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file is autogenerated from tools provided by Altera.*/ +#ifndef __SDRAM_CONFIG_H +#define __SDRAM_CONFIG_H + +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_MEMTYPE 2 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_MEMBL 8 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_ADDRORDER 0 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_ECCEN 1 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_ECCCORREN 1 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_REORDEREN 1 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_STARVELIMIT 10 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_DQSTRKEN 0 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_NODMPINS 0 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TCWL 6 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_AL 0 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TCL 7 +#ifdef CONFIG_SOCFPGA_ARRIA5 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TRRD 4 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TFAW 19 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TRFC 139 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TREFI 4160 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TRCD 8 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TRP 8 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TWR 8 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TWTR 4 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRTP 4 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRAS 19 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRC 26 +#else +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TRRD 3 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TFAW 14 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TRFC 104 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TREFI 3120 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TRCD 6 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TRP 6 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TWR 6 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TWTR 4 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRTP 3 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRAS 14 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRC 20 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TMRD 4 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TCCD 4 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING4_SELFRFSHEXIT 512 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING4_PWRDOWNEXIT 3 +#define CONFIG_HPS_SDR_CTRLCFG_LOWPWRTIMING_AUTOPDCYCLES 0 +#define CONFIG_HPS_SDR_CTRLCFG_LOWPWRTIMING_CLKDISABLECYCLES 8 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_COLBITS 10 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS 15 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_BANKBITS 3 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_CSBITS 1 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMIFWIDTH_IFWIDTH 40 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMDEVWIDTH_DEVWIDTH 8 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMINTR_INTREN 0 +#define CONFIG_HPS_SDR_CTRLCFG_LOWPWREQ_SELFRFSHMASK 3 +#define CONFIG_HPS_SDR_CTRLCFG_STATICCFG_MEMBL 2 +#define CONFIG_HPS_SDR_CTRLCFG_STATICCFG_USEECCASDATA 0 +#define CONFIG_HPS_SDR_CTRLCFG_CTRLWIDTH_CTRLWIDTH 2 +#define CONFIG_HPS_SDR_CTRLCFG_PORTCFG_AUTOPCHEN 0 +#define CONFIG_HPS_SDR_CTRLCFG_FIFOCFG_SYNCMODE 0 +#define CONFIG_HPS_SDR_CTRLCFG_FIFOCFG_INCSYNC 0 +#define CONFIG_HPS_SDR_CTRLCFG_MPPRIORITY_USERPRIORITY 0x3FFD1088 +#define CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_0_STATICWEIGHT_31_0 0x21084210 +#define CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_1_STATICWEIGHT_49_32 0x1EF84 +#define CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_1_SUMOFWEIGHT_13_0 0x2020 +#define CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_2_SUMOFWEIGHT_45_14 0x0 +#define CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_3_SUMOFWEIGHT_63_46 0xF800 +#define CONFIG_HPS_SDR_CTRLCFG_PHYCTRL_PHYCTRL_0 0x200 + +#define CONFIG_HPS_SDR_CTRLCFG_CPORTWIDTH_CPORTWIDTH 0x44555 +#define CONFIG_HPS_SDR_CTRLCFG_CPORTWMAP_CPORTWMAP 0x2C011000 +#define CONFIG_HPS_SDR_CTRLCFG_CPORTRMAP_CPORTRMAP 0xB00088 +#define CONFIG_HPS_SDR_CTRLCFG_RFIFOCMAP_RFIFOCMAP 0x760210 +#define CONFIG_HPS_SDR_CTRLCFG_WFIFOCMAP_WFIFOCMAP 0x980543 +#define CONFIG_HPS_SDR_CTRLCFG_CPORTRDWR_CPORTRDWR 0x5A56A +#define CONFIG_HPS_SDR_CTRLCFG_MPPACING_0_THRESHOLD1_31_0 0x20820820 +#define CONFIG_HPS_SDR_CTRLCFG_MPPACING_1_THRESHOLD1_59_32 0x8208208 +#define CONFIG_HPS_SDR_CTRLCFG_MPPACING_1_THRESHOLD2_3_0 0 +#define CONFIG_HPS_SDR_CTRLCFG_MPPACING_2_THRESHOLD2_35_4 0x41041041 +#define CONFIG_HPS_SDR_CTRLCFG_MPPACING_3_THRESHOLD2_59_36 0x410410 +#define CONFIG_HPS_SDR_CTRLCFG_MPTHRESHOLDRST_0_THRESHOLDRSTCYCLES_31_0 \ +0x01010101 +#define CONFIG_HPS_SDR_CTRLCFG_MPTHRESHOLDRST_1_THRESHOLDRSTCYCLES_63_32 \ +0x01010101 +#define CONFIG_HPS_SDR_CTRLCFG_MPTHRESHOLDRST_2_THRESHOLDRSTCYCLES_79_64 \ +0x0101 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMODT_READ 0 +#define CONFIG_HPS_SDR_CTRLCFG_DRAMODT_WRITE 1 +#define CONFIG_HPS_SDR_CTRLCFG_FPGAPORTRST_READ_PORT_USED 0 +#define CONFIG_HPS_SDR_CTRLCFG_FPGAPORTRST_WRITE_PORT_USED 0 +#define CONFIG_HPS_SDR_CTRLCFG_FPGAPORTRST_COMMAND_PORT_USED 0 +#define CONFIG_HPS_SDR_CTRLCFG_FPGAPORTRST 0 + +#endif /*#ifndef__SDRAM_CONFIG_H*/ diff --git a/board/altera/socfpga/qts/sequencer_auto.h b/board/altera/socfpga/qts/sequencer_auto.h new file mode 100644 index 00000000000..0c5d83bbdff --- /dev/null +++ b/board/altera/socfpga/qts/sequencer_auto.h @@ -0,0 +1,128 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#define RW_MGR_READ_B2B_WAIT2 0x6A +#define RW_MGR_LFSR_WR_RD_BANK_0_WAIT 0x31 +#define RW_MGR_REFRESH_ALL 0x14 +#define RW_MGR_ZQCL 0x06 +#define RW_MGR_LFSR_WR_RD_BANK_0_NOP 0x22 +#define RW_MGR_LFSR_WR_RD_BANK_0_DQS 0x23 +#define RW_MGR_ACTIVATE_0_AND_1 0x0D +#define RW_MGR_MRS2_MIRR 0x0A +#define RW_MGR_INIT_RESET_0_CKE_0 0x6E +#define RW_MGR_LFSR_WR_RD_DM_BANK_0_WAIT 0x45 +#define RW_MGR_ACTIVATE_1 0x0F +#define RW_MGR_MRS2 0x04 +#define RW_MGR_LFSR_WR_RD_DM_BANK_0_WL_1 0x34 +#define RW_MGR_MRS1 0x03 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define RW_MGR_IDLE_LOOP1 0x7A +#else +#define RW_MGR_IDLE_LOOP1 0x7C +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define RW_MGR_GUARANTEED_WRITE_WAIT2 0x18 +#define RW_MGR_MRS3 0x05 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define RW_MGR_IDLE_LOOP2 0x79 +#else +#define RW_MGR_IDLE_LOOP2 0x7B +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define RW_MGR_GUARANTEED_WRITE_WAIT1 0x1E +#define RW_MGR_LFSR_WR_RD_BANK_0_DATA 0x24 +#define RW_MGR_GUARANTEED_WRITE_WAIT3 0x1C +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define RW_MGR_RDIMM_CMD 0x78 +#else +#define RW_MGR_RDIMM_CMD 0x7A +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP 0x36 +#define RW_MGR_GUARANTEED_WRITE_WAIT0 0x1A +#define RW_MGR_LFSR_WR_RD_DM_BANK_0_DATA 0x38 +#define RW_MGR_GUARANTEED_READ_CONT 0x53 +#define RW_MGR_MRS3_MIRR 0x0B +#define RW_MGR_IDLE 0x00 +#define RW_MGR_READ_B2B 0x58 +#define RW_MGR_INIT_RESET_0_CKE_0_inloop 0x6F +#define RW_MGR_LFSR_WR_RD_DM_BANK_0_DQS 0x37 +#define RW_MGR_GUARANTEED_WRITE 0x17 +#define RW_MGR_PRECHARGE_ALL 0x12 +#define RW_MGR_INIT_RESET_1_CKE_0_inloop_1 0x74 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define RW_MGR_SGLE_READ 0x7C +#else +#define RW_MGR_SGLE_READ 0x7E +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define RW_MGR_MRS0_USER_MIRR 0x0C +#define RW_MGR_RETURN 0x01 +#define RW_MGR_LFSR_WR_RD_DM_BANK_0 0x35 +#define RW_MGR_MRS0_USER 0x07 +#define RW_MGR_GUARANTEED_READ 0x4B +#define RW_MGR_MRS0_DLL_RESET_MIRR 0x08 +#define RW_MGR_INIT_RESET_1_CKE_0 0x73 +#define RW_MGR_ACTIVATE_0_AND_1_WAIT2 0x10 +#define RW_MGR_LFSR_WR_RD_BANK_0_WL_1 0x20 +#define RW_MGR_MRS0_DLL_RESET 0x02 +#define RW_MGR_ACTIVATE_0_AND_1_WAIT1 0x0E +#define RW_MGR_LFSR_WR_RD_BANK_0 0x21 +#define RW_MGR_CLEAR_DQS_ENABLE 0x48 +#define RW_MGR_MRS1_MIRR 0x09 +#define RW_MGR_READ_B2B_WAIT1 0x60 +#define RW_MGR_CONTENT_READ_B2B_WAIT2 0x00C680 +#define RW_MGR_CONTENT_LFSR_WR_RD_BANK_0_WAIT 0x00A680 +#define RW_MGR_CONTENT_REFRESH_ALL 0x000980 +#define RW_MGR_CONTENT_ZQCL 0x008380 +#define RW_MGR_CONTENT_LFSR_WR_RD_BANK_0_NOP 0x00E700 +#define RW_MGR_CONTENT_LFSR_WR_RD_BANK_0_DQS 0x000C00 +#define RW_MGR_CONTENT_ACTIVATE_0_AND_1 0x000800 +#define RW_MGR_CONTENT_MRS2_MIRR 0x008580 +#define RW_MGR_CONTENT_INIT_RESET_0_CKE_0 0x000000 +#define RW_MGR_CONTENT_LFSR_WR_RD_DM_BANK_0_WAIT 0x00A680 +#define RW_MGR_CONTENT_ACTIVATE_1 0x000880 +#define RW_MGR_CONTENT_MRS2 0x008280 +#define RW_MGR_CONTENT_LFSR_WR_RD_DM_BANK_0_WL_1 0x00CE00 +#define RW_MGR_CONTENT_MRS1 0x008200 +#define RW_MGR_CONTENT_IDLE_LOOP1 0x00A680 +#define RW_MGR_CONTENT_GUARANTEED_WRITE_WAIT2 0x00CCE8 +#define RW_MGR_CONTENT_MRS3 0x008300 +#define RW_MGR_CONTENT_IDLE_LOOP2 0x008680 +#define RW_MGR_CONTENT_GUARANTEED_WRITE_WAIT1 0x00AC88 +#define RW_MGR_CONTENT_LFSR_WR_RD_BANK_0_DATA 0x020CE0 +#define RW_MGR_CONTENT_GUARANTEED_WRITE_WAIT3 0x00EC88 +#define RW_MGR_CONTENT_RDIMM_CMD 0x009180 +#define RW_MGR_CONTENT_LFSR_WR_RD_DM_BANK_0_NOP 0x00E700 +#define RW_MGR_CONTENT_GUARANTEED_WRITE_WAIT0 0x008CE8 +#define RW_MGR_CONTENT_LFSR_WR_RD_DM_BANK_0_DATA 0x030CE0 +#define RW_MGR_CONTENT_GUARANTEED_READ_CONT 0x001168 +#define RW_MGR_CONTENT_MRS3_MIRR 0x008600 +#define RW_MGR_CONTENT_IDLE 0x080000 +#define RW_MGR_CONTENT_READ_B2B 0x040E88 +#define RW_MGR_CONTENT_INIT_RESET_0_CKE_0_inloop 0x000000 +#define RW_MGR_CONTENT_LFSR_WR_RD_DM_BANK_0_DQS 0x000C00 +#define RW_MGR_CONTENT_GUARANTEED_WRITE 0x000B68 +#define RW_MGR_CONTENT_PRECHARGE_ALL 0x000900 +#define RW_MGR_CONTENT_INIT_RESET_1_CKE_0_inloop_1 0x000080 +#define RW_MGR_CONTENT_SGLE_READ 0x040F08 +#define RW_MGR_CONTENT_MRS0_USER_MIRR 0x008400 +#define RW_MGR_CONTENT_RETURN 0x080680 +#define RW_MGR_CONTENT_LFSR_WR_RD_DM_BANK_0 0x00CD80 +#define RW_MGR_CONTENT_MRS0_USER 0x008100 +#define RW_MGR_CONTENT_GUARANTEED_READ 0x001168 +#define RW_MGR_CONTENT_MRS0_DLL_RESET_MIRR 0x008480 +#define RW_MGR_CONTENT_INIT_RESET_1_CKE_0 0x000080 +#define RW_MGR_CONTENT_ACTIVATE_0_AND_1_WAIT2 0x00A680 +#define RW_MGR_CONTENT_LFSR_WR_RD_BANK_0_WL_1 0x00CE00 +#define RW_MGR_CONTENT_MRS0_DLL_RESET 0x008180 +#define RW_MGR_CONTENT_ACTIVATE_0_AND_1_WAIT1 0x008680 +#define RW_MGR_CONTENT_LFSR_WR_RD_BANK_0 0x00CD80 +#define RW_MGR_CONTENT_CLEAR_DQS_ENABLE 0x001158 +#define RW_MGR_CONTENT_MRS1_MIRR 0x008500 +#define RW_MGR_CONTENT_READ_B2B_WAIT1 0x00A680 + diff --git a/board/altera/socfpga/qts/sequencer_auto_ac_init.h b/board/altera/socfpga/qts/sequencer_auto_ac_init.h new file mode 100644 index 00000000000..c46421b2120 --- /dev/null +++ b/board/altera/socfpga/qts/sequencer_auto_ac_init.h @@ -0,0 +1,84 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +const uint32_t ac_rom_init[] = { +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ + 0x20700000, + 0x20780000, + 0x10080831, + 0x10080930, + 0x10090004, + 0x100a0008, + 0x100b0000, + 0x10380400, + 0x10080849, + 0x100808c8, + 0x100a0004, + 0x10090010, + 0x100b0000, + 0x30780000, + 0x38780000, + 0x30780000, + 0x10680000, + 0x106b0000, + 0x10280400, + 0x10480000, + 0x1c980000, + 0x1c9b0000, + 0x1c980008, + 0x1c9b0008, + 0x38f80000, + 0x3cf80000, + 0x38780000, + 0x18180000, + 0x18980000, + 0x13580000, + 0x135b0000, + 0x13580008, + 0x135b0008, + 0x33780000, + 0x10580008, + 0x10780000 +#else + 0x20700000, + 0x20780000, + 0x10080431, + 0x10080530, + 0x10090004, + 0x100a0008, + 0x100b0000, + 0x10380400, + 0x10080449, + 0x100804c8, + 0x100a0004, + 0x10090010, + 0x100b0000, + 0x30780000, + 0x38780000, + 0x30780000, + 0x10680000, + 0x106b0000, + 0x10280400, + 0x10480000, + 0x1c980000, + 0x1c9b0000, + 0x1c980008, + 0x1c9b0008, + 0x38f80000, + 0x3cf80000, + 0x38780000, + 0x18180000, + 0x18980000, + 0x13580000, + 0x135b0000, + 0x13580008, + 0x135b0008, + 0x33780000, + 0x10580008, + 0x10780000 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +}; diff --git a/board/altera/socfpga/qts/sequencer_auto_inst_init.h b/board/altera/socfpga/qts/sequencer_auto_inst_init.h new file mode 100644 index 00000000000..ad0395b4961 --- /dev/null +++ b/board/altera/socfpga/qts/sequencer_auto_inst_init.h @@ -0,0 +1,268 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +const u32 inst_rom_init[] = { + 0x80000, + 0x80680, + 0x8180, + 0x8200, + 0x8280, + 0x8300, + 0x8380, + 0x8100, + 0x8480, + 0x8500, + 0x8580, + 0x8600, + 0x8400, + 0x800, + 0x8680, + 0x880, + 0xa680, + 0x80680, + 0x900, + 0x80680, + 0x980, + 0x8680, + 0x80680, + 0xb68, + 0xcce8, + 0xae8, + 0x8ce8, + 0xb88, + 0xec88, + 0xa08, + 0xac88, + 0x80680, + 0xce00, + 0xcd80, + 0xe700, + 0xc00, + 0x20ce0, + 0x20ce0, + 0x20ce0, + 0x20ce0, + 0xd00, + 0x680, + 0x680, + 0x680, + 0x680, + 0x60e80, + 0x61080, + 0x61080, + 0x61080, + 0xa680, + 0x8680, + 0x80680, + 0xce00, + 0xcd80, + 0xe700, + 0xc00, + 0x30ce0, + 0x30ce0, + 0x30ce0, + 0x30ce0, + 0xd00, + 0x680, + 0x680, + 0x680, + 0x680, + 0x70e80, + 0x71080, + 0x71080, + 0x71080, + 0xa680, + 0x8680, + 0x80680, + 0x1158, + 0x6d8, + 0x80680, + 0x1168, + 0x7e8, + 0x7e8, + 0x87e8, + 0x40fe8, + 0x410e8, + 0x410e8, + 0x410e8, + 0x1168, + 0x7e8, + 0x7e8, + 0xa7e8, + 0x80680, + 0x40e88, + 0x41088, + 0x41088, + 0x41088, + 0x40f68, + 0x410e8, + 0x410e8, + 0x410e8, + 0xa680, + 0x40fe8, + 0x410e8, + 0x410e8, + 0x410e8, + 0x41008, + 0x41088, + 0x41088, + 0x41088, + 0x1100, + 0xc680, + 0x8680, + 0xe680, + 0x80680, + 0x0, + 0x8000, + 0xa000, + 0xc000, + 0x80000, + 0x80, + 0x8080, + 0xa080, + 0xc080, + 0x80080, + 0x9180, + 0x8680, + 0xa680, + 0x80680, + 0x40f08, + 0x80680 +}; +#else +const u32 inst_rom_init[] = { + 0x80000, + 0x80680, + 0x8180, + 0x8200, + 0x8280, + 0x8300, + 0x8380, + 0x8100, + 0x8480, + 0x8500, + 0x8580, + 0x8600, + 0x8400, + 0x800, + 0x8680, + 0x880, + 0xa680, + 0x80680, + 0x900, + 0x80680, + 0x980, + 0x8680, + 0x80680, + 0xb68, + 0xcce8, + 0xae8, + 0x8ce8, + 0xb88, + 0xec88, + 0xa08, + 0xac88, + 0x80680, + 0xce00, + 0xcd80, + 0xe700, + 0xc00, + 0x20ce0, + 0x20ce0, + 0x20ce0, + 0x20ce0, + 0xd00, + 0x680, + 0x680, + 0x680, + 0x680, + 0x60e80, + 0x61080, + 0x61080, + 0x61080, + 0xa680, + 0x8680, + 0x80680, + 0xce00, + 0xcd80, + 0xe700, + 0xc00, + 0x30ce0, + 0x30ce0, + 0x30ce0, + 0x30ce0, + 0xd00, + 0x680, + 0x680, + 0x680, + 0x680, + 0x70e80, + 0x71080, + 0x71080, + 0x71080, + 0xa680, + 0x8680, + 0x80680, + 0x1158, + 0x6d8, + 0x80680, + 0x1168, + 0x7e8, + 0x7e8, + 0x87e8, + 0x40fe8, + 0x410e8, + 0x410e8, + 0x410e8, + 0x1168, + 0x7e8, + 0x7e8, + 0xa7e8, + 0x80680, + 0x40e88, + 0x41088, + 0x41088, + 0x41088, + 0x40f68, + 0x410e8, + 0x410e8, + 0x410e8, + 0xa680, + 0x40fe8, + 0x410e8, + 0x410e8, + 0x410e8, + 0x41008, + 0x41088, + 0x41088, + 0x41088, + 0x1100, + 0xc680, + 0x8680, + 0xe680, + 0x80680, + 0x0, + 0x0, + 0xa000, + 0x8000, + 0x80000, + 0x80, + 0x80, + 0x80, + 0x80, + 0xa080, + 0x8080, + 0x80080, + 0x9180, + 0x8680, + 0xa680, + 0x80680, + 0x40f08, + 0x80680 +}; +#endif /* CONFIG_SOCFPGA_ARRIA5 */ diff --git a/board/altera/socfpga/qts/sequencer_defines.h b/board/altera/socfpga/qts/sequencer_defines.h new file mode 100644 index 00000000000..bfe5b2719fd --- /dev/null +++ b/board/altera/socfpga/qts/sequencer_defines.h @@ -0,0 +1,122 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _SEQUENCER_DEFINES_H_ +#define _SEQUENCER_DEFINES_H_ + +#define AC_ROM_MR1_MIRR 0000000000100 +#define AC_ROM_MR1_OCD_ENABLE +#define AC_ROM_MR2_MIRR 0000000010000 +#define AC_ROM_MR3_MIRR 0000000000000 +#define AC_ROM_MR0_CALIB +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define AC_ROM_MR0_DLL_RESET_MIRR 0100011001000 +#define AC_ROM_MR0_DLL_RESET 0100100110000 +#define AC_ROM_MR0_MIRR 0100001001001 +#define AC_ROM_MR0 0100000110001 +#else +#define AC_ROM_MR0_DLL_RESET_MIRR 0010011001000 +#define AC_ROM_MR0_DLL_RESET 0010100110000 +#define AC_ROM_MR0_MIRR 0010001001001 +#define AC_ROM_MR0 0010000110001 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define AC_ROM_MR1 0000000000100 +#define AC_ROM_MR2 0000000001000 +#define AC_ROM_MR3 0000000000000 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define AFI_CLK_FREQ 534 +#else +#define AFI_CLK_FREQ 401 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define AFI_RATE_RATIO 1 +#define AVL_CLK_FREQ 67 +#define BFM_MODE 0 +#define BURST2 0 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define CALIB_LFIFO_OFFSET 8 +#define CALIB_VFIFO_OFFSET 6 +#else +#define CALIB_LFIFO_OFFSET 7 +#define CALIB_VFIFO_OFFSET 5 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define ENABLE_EXPORT_SEQ_DEBUG_BRIDGE 0 +#define ENABLE_SUPER_QUICK_CALIBRATION 0 +#define GUARANTEED_READ_BRINGUP_TEST 0 +#define HARD_PHY 1 +#define HARD_VFIFO 1 +#define HPS_HW 1 +#define HR_DDIO_OUT_HAS_THREE_REGS 0 +#define IO_DELAY_PER_DCHAIN_TAP 25 +#define IO_DELAY_PER_DQS_EN_DCHAIN_TAP 25 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define IO_DELAY_PER_OPA_TAP 234 +#else +#define IO_DELAY_PER_OPA_TAP 312 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define IO_DLL_CHAIN_LENGTH 8 +#define IO_DM_OUT_RESERVE 0 +#define IO_DQDQS_OUT_PHASE_MAX 0 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define IO_DQS_EN_DELAY_MAX 15 +#define IO_DQS_EN_DELAY_OFFSET 16 +#else +#define IO_DQS_EN_DELAY_MAX 31 +#define IO_DQS_EN_DELAY_OFFSET 0 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define IO_DQS_EN_PHASE_MAX 7 +#define IO_DQS_IN_DELAY_MAX 31 +#define IO_DQS_IN_RESERVE 4 +#define IO_DQS_OUT_RESERVE 6 +#define IO_DQ_OUT_RESERVE 0 +#define IO_IO_IN_DELAY_MAX 31 +#define IO_IO_OUT1_DELAY_MAX 31 +#define IO_IO_OUT2_DELAY_MAX 0 +#define IO_SHIFT_DQS_EN_WHEN_SHIFT_DQS 0 +#define MARGIN_VARIATION_TEST 0 +#define MAX_LATENCY_COUNT_WIDTH 5 +#define MEM_ADDR_WIDTH 13 +#define READ_VALID_FIFO_SIZE 16 +#ifdef CONFIG_SOCFPGA_ARRIA5 +/* The if..else... is not required if generated by tools */ +#define REG_FILE_INIT_SEQ_SIGNATURE 0x5555048c +#else +#define REG_FILE_INIT_SEQ_SIGNATURE 0x55550483 +#endif /* CONFIG_SOCFPGA_ARRIA5 */ +#define RW_MGR_MEM_ADDRESS_MIRRORING 0 +#define RW_MGR_MEM_ADDRESS_WIDTH 15 +#define RW_MGR_MEM_BANK_WIDTH 3 +#define RW_MGR_MEM_CHIP_SELECT_WIDTH 1 +#define RW_MGR_MEM_CLK_EN_WIDTH 1 +#define RW_MGR_MEM_CONTROL_WIDTH 1 +#define RW_MGR_MEM_DATA_MASK_WIDTH 5 +#define RW_MGR_MEM_DATA_WIDTH 40 +#define RW_MGR_MEM_DQ_PER_READ_DQS 8 +#define RW_MGR_MEM_DQ_PER_WRITE_DQS 8 +#define RW_MGR_MEM_IF_READ_DQS_WIDTH 5 +#define RW_MGR_MEM_IF_WRITE_DQS_WIDTH 5 +#define RW_MGR_MEM_NUMBER_OF_CS_PER_DIMM 1 +#define RW_MGR_MEM_NUMBER_OF_RANKS 1 +#define RW_MGR_MEM_ODT_WIDTH 1 +#define RW_MGR_MEM_VIRTUAL_GROUPS_PER_READ_DQS 1 +#define RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS 1 +#define RW_MGR_MR0_BL 1 +#define RW_MGR_MR0_CAS_LATENCY 3 +#define RW_MGR_TRUE_MEM_DATA_MASK_WIDTH 5 +#define RW_MGR_WRITE_TO_DEBUG_READ 1.0 +#define SKEW_CALIBRATION 0 +#define TINIT_CNTR1_VAL 32 +#define TINIT_CNTR2_VAL 32 +#define TINIT_CNTR0_VAL 132 +#define TRESET_CNTR1_VAL 99 +#define TRESET_CNTR2_VAL 10 +#define TRESET_CNTR0_VAL 132 + +#endif /* _SEQUENCER_DEFINES_H_ */ diff --git a/board/altera/socfpga/wrap_iocsr_config.c b/board/altera/socfpga/wrap_iocsr_config.c new file mode 100644 index 00000000000..49e922823f3 --- /dev/null +++ b/board/altera/socfpga/wrap_iocsr_config.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/clock_manager.h> +/* + * Yes, dear reader, we're including a C file here, this is no mistake :-) + */ +#include "qts/iocsr_config.c" + +int iocsr_get_config_table(const unsigned int chain_id, + const unsigned long **table, + unsigned int *table_len) +{ + switch (chain_id) { + case 0: + *table = iocsr_scan_chain0_table; + *table_len = CONFIG_HPS_IOCSR_SCANCHAIN0_LENGTH; + break; + case 1: + *table = iocsr_scan_chain1_table; + *table_len = CONFIG_HPS_IOCSR_SCANCHAIN1_LENGTH; + break; + case 2: + *table = iocsr_scan_chain2_table; + *table_len = CONFIG_HPS_IOCSR_SCANCHAIN2_LENGTH; + break; + case 3: + *table = iocsr_scan_chain3_table; + *table_len = CONFIG_HPS_IOCSR_SCANCHAIN3_LENGTH; + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/board/altera/socfpga/wrap_pinmux_config.c b/board/altera/socfpga/wrap_pinmux_config.c new file mode 100644 index 00000000000..b33e2cab824 --- /dev/null +++ b/board/altera/socfpga/wrap_pinmux_config.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +/* + * Yes, dear reader, we're including a C file here, this is no mistake. + * But this time around, we do even more perverse hacking here to be + * compatible with QTS headers and obtain reasonably nice results too. + * + * First, we define _PRELOADER_PINMUX_CONFIG_H_, which will neutralise + * the pinmux_config.h inclusion in pinmux_config.c . Since we are + * probing everything from DT, we do NOT want those macros from the + * pinmux_config.h to ooze into our build system, anywhere, ever. So + * we nip it at the bud. + * + * Next, pinmux_config.c needs CONFIG_HPS_PINMUX_NUM and uses it to + * specify sized array explicitly. Instead, we want to use ARRAY_SIZE + * to figure out the size of the array, so define this macro as an + * empty one, so that the preprocessor optimizes things such that the + * arrays are not sized by default. + */ +#define _PRELOADER_PINMUX_CONFIG_H_ +#define CONFIG_HPS_PINMUX_NUM +#include "qts/pinmux_config.c" + +void sysmgr_get_pinmux_table(const unsigned long **table, + unsigned int *table_len) +{ + *table = sys_mgr_init_table; + *table_len = ARRAY_SIZE(sys_mgr_init_table); +} diff --git a/board/altera/socfpga/wrap_pll_config.c b/board/altera/socfpga/wrap_pll_config.c new file mode 100644 index 00000000000..8dbff68f5cc --- /dev/null +++ b/board/altera/socfpga/wrap_pll_config.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015 Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/clock_manager.h> +#include "qts/pll_config.h" + +#define MAIN_VCO_BASE ( \ + (CONFIG_HPS_MAINPLLGRP_VCO_DENOM << \ + CLKMGR_MAINPLLGRP_VCO_DENOM_OFFSET) | \ + (CONFIG_HPS_MAINPLLGRP_VCO_NUMER << \ + CLKMGR_MAINPLLGRP_VCO_NUMER_OFFSET) \ + ) + +#define PERI_VCO_BASE ( \ + (CONFIG_HPS_PERPLLGRP_VCO_PSRC << \ + CLKMGR_PERPLLGRP_VCO_PSRC_OFFSET) | \ + (CONFIG_HPS_PERPLLGRP_VCO_DENOM << \ + CLKMGR_PERPLLGRP_VCO_DENOM_OFFSET) | \ + (CONFIG_HPS_PERPLLGRP_VCO_NUMER << \ + CLKMGR_PERPLLGRP_VCO_NUMER_OFFSET) \ + ) + +#define SDR_VCO_BASE ( \ + (CONFIG_HPS_SDRPLLGRP_VCO_SSRC << \ + CLKMGR_SDRPLLGRP_VCO_SSRC_OFFSET) | \ + (CONFIG_HPS_SDRPLLGRP_VCO_DENOM << \ + CLKMGR_SDRPLLGRP_VCO_DENOM_OFFSET) | \ + (CONFIG_HPS_SDRPLLGRP_VCO_NUMER << \ + CLKMGR_SDRPLLGRP_VCO_NUMER_OFFSET) \ + ) + +static const struct cm_config cm_default_cfg = { + /* main group */ + MAIN_VCO_BASE, + (CONFIG_HPS_MAINPLLGRP_MPUCLK_CNT << + CLKMGR_MAINPLLGRP_MPUCLK_CNT_OFFSET), + (CONFIG_HPS_MAINPLLGRP_MAINCLK_CNT << + CLKMGR_MAINPLLGRP_MAINCLK_CNT_OFFSET), + (CONFIG_HPS_MAINPLLGRP_DBGATCLK_CNT << + CLKMGR_MAINPLLGRP_DBGATCLK_CNT_OFFSET), + (CONFIG_HPS_MAINPLLGRP_MAINQSPICLK_CNT << + CLKMGR_MAINPLLGRP_MAINQSPICLK_CNT_OFFSET), + (CONFIG_HPS_MAINPLLGRP_MAINNANDSDMMCCLK_CNT << + CLKMGR_PERPLLGRP_PERNANDSDMMCCLK_CNT_OFFSET), + (CONFIG_HPS_MAINPLLGRP_CFGS2FUSER0CLK_CNT << + CLKMGR_MAINPLLGRP_CFGS2FUSER0CLK_CNT_OFFSET), + (CONFIG_HPS_MAINPLLGRP_MAINDIV_L3MPCLK << + CLKMGR_MAINPLLGRP_MAINDIV_L3MPCLK_OFFSET) | + (CONFIG_HPS_MAINPLLGRP_MAINDIV_L3SPCLK << + CLKMGR_MAINPLLGRP_MAINDIV_L3SPCLK_OFFSET) | + (CONFIG_HPS_MAINPLLGRP_MAINDIV_L4MPCLK << + CLKMGR_MAINPLLGRP_MAINDIV_L4MPCLK_OFFSET) | + (CONFIG_HPS_MAINPLLGRP_MAINDIV_L4SPCLK << + CLKMGR_MAINPLLGRP_MAINDIV_L4SPCLK_OFFSET), + (CONFIG_HPS_MAINPLLGRP_DBGDIV_DBGATCLK << + CLKMGR_MAINPLLGRP_DBGDIV_DBGATCLK_OFFSET) | + (CONFIG_HPS_MAINPLLGRP_DBGDIV_DBGCLK << + CLKMGR_MAINPLLGRP_DBGDIV_DBGCLK_OFFSET), + (CONFIG_HPS_MAINPLLGRP_TRACEDIV_TRACECLK << + CLKMGR_MAINPLLGRP_TRACEDIV_TRACECLK_OFFSET), + (CONFIG_HPS_MAINPLLGRP_L4SRC_L4MP << + CLKMGR_MAINPLLGRP_L4SRC_L4MP_OFFSET) | + (CONFIG_HPS_MAINPLLGRP_L4SRC_L4SP << + CLKMGR_MAINPLLGRP_L4SRC_L4SP_OFFSET), + + /* peripheral group */ + PERI_VCO_BASE, + (CONFIG_HPS_PERPLLGRP_EMAC0CLK_CNT << + CLKMGR_PERPLLGRP_EMAC0CLK_CNT_OFFSET), + (CONFIG_HPS_PERPLLGRP_EMAC1CLK_CNT << + CLKMGR_PERPLLGRP_EMAC1CLK_CNT_OFFSET), + (CONFIG_HPS_PERPLLGRP_PERQSPICLK_CNT << + CLKMGR_PERPLLGRP_PERQSPICLK_CNT_OFFSET), + (CONFIG_HPS_PERPLLGRP_PERNANDSDMMCCLK_CNT << + CLKMGR_PERPLLGRP_PERNANDSDMMCCLK_CNT_OFFSET), + (CONFIG_HPS_PERPLLGRP_PERBASECLK_CNT << + CLKMGR_PERPLLGRP_PERBASECLK_CNT_OFFSET), + (CONFIG_HPS_PERPLLGRP_S2FUSER1CLK_CNT << + CLKMGR_PERPLLGRP_S2FUSER1CLK_CNT_OFFSET), + (CONFIG_HPS_PERPLLGRP_DIV_USBCLK << + CLKMGR_PERPLLGRP_DIV_USBCLK_OFFSET) | + (CONFIG_HPS_PERPLLGRP_DIV_SPIMCLK << + CLKMGR_PERPLLGRP_DIV_SPIMCLK_OFFSET) | + (CONFIG_HPS_PERPLLGRP_DIV_CAN0CLK << + CLKMGR_PERPLLGRP_DIV_CAN0CLK_OFFSET) | + (CONFIG_HPS_PERPLLGRP_DIV_CAN1CLK << + CLKMGR_PERPLLGRP_DIV_CAN1CLK_OFFSET), + (CONFIG_HPS_PERPLLGRP_GPIODIV_GPIODBCLK << + CLKMGR_PERPLLGRP_GPIODIV_GPIODBCLK_OFFSET), + (CONFIG_HPS_PERPLLGRP_SRC_QSPI << + CLKMGR_PERPLLGRP_SRC_QSPI_OFFSET) | + (CONFIG_HPS_PERPLLGRP_SRC_NAND << + CLKMGR_PERPLLGRP_SRC_NAND_OFFSET) | + (CONFIG_HPS_PERPLLGRP_SRC_SDMMC << + CLKMGR_PERPLLGRP_SRC_SDMMC_OFFSET), + + /* sdram pll group */ + SDR_VCO_BASE, + (CONFIG_HPS_SDRPLLGRP_DDRDQSCLK_PHASE << + CLKMGR_SDRPLLGRP_DDRDQSCLK_PHASE_OFFSET) | + (CONFIG_HPS_SDRPLLGRP_DDRDQSCLK_CNT << + CLKMGR_SDRPLLGRP_DDRDQSCLK_CNT_OFFSET), + (CONFIG_HPS_SDRPLLGRP_DDR2XDQSCLK_PHASE << + CLKMGR_SDRPLLGRP_DDR2XDQSCLK_PHASE_OFFSET) | + (CONFIG_HPS_SDRPLLGRP_DDR2XDQSCLK_CNT << + CLKMGR_SDRPLLGRP_DDR2XDQSCLK_CNT_OFFSET), + (CONFIG_HPS_SDRPLLGRP_DDRDQCLK_PHASE << + CLKMGR_SDRPLLGRP_DDRDQCLK_PHASE_OFFSET) | + (CONFIG_HPS_SDRPLLGRP_DDRDQCLK_CNT << + CLKMGR_SDRPLLGRP_DDRDQCLK_CNT_OFFSET), + (CONFIG_HPS_SDRPLLGRP_S2FUSER2CLK_PHASE << + CLKMGR_SDRPLLGRP_S2FUSER2CLK_PHASE_OFFSET) | + (CONFIG_HPS_SDRPLLGRP_S2FUSER2CLK_CNT << + CLKMGR_SDRPLLGRP_S2FUSER2CLK_CNT_OFFSET), +}; + +const struct cm_config * const cm_get_default_config(void) +{ + return &cm_default_cfg; +} + +const unsigned int cm_get_osc_clk_hz(const int osc) +{ + if (osc == 1) + return CONFIG_HPS_CLK_OSC1_HZ; + else if (osc == 2) + return CONFIG_HPS_CLK_OSC2_HZ; + else + return 0; +} + +const unsigned int cm_get_f2s_per_ref_clk_hz(void) +{ + return CONFIG_HPS_CLK_F2S_PER_REF_HZ; +} + +const unsigned int cm_get_f2s_sdr_ref_clk_hz(void) +{ + return CONFIG_HPS_CLK_F2S_SDR_REF_HZ; +} diff --git a/board/altera/socfpga/wrap_sdram_config.c b/board/altera/socfpga/wrap_sdram_config.c new file mode 100644 index 00000000000..cd97cc509f3 --- /dev/null +++ b/board/altera/socfpga/wrap_sdram_config.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2015 Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/sdram.h> +/* QTS output file. */ +#include "qts/sdram_config.h" + +#include "qts/sequencer_auto_ac_init.h" +#include "qts/sequencer_auto_inst_init.h" +#include "qts/sequencer_auto.h" +#include "qts/sequencer_defines.h" + +static const struct socfpga_sdram_config sdram_config = { + .ctrl_cfg = + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_MEMTYPE << + SDR_CTRLGRP_CTRLCFG_MEMTYPE_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_MEMBL << + SDR_CTRLGRP_CTRLCFG_MEMBL_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_ADDRORDER << + SDR_CTRLGRP_CTRLCFG_ADDRORDER_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_ECCEN << + SDR_CTRLGRP_CTRLCFG_ECCEN_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_ECCCORREN << + SDR_CTRLGRP_CTRLCFG_ECCCORREN_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_REORDEREN << + SDR_CTRLGRP_CTRLCFG_REORDEREN_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_STARVELIMIT << + SDR_CTRLGRP_CTRLCFG_STARVELIMIT_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_DQSTRKEN << + SDR_CTRLGRP_CTRLCFG_DQSTRKEN_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_NODMPINS << + SDR_CTRLGRP_CTRLCFG_NODMPINS_LSB), + .dram_timing1 = + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TCWL << + SDR_CTRLGRP_DRAMTIMING1_TCWL_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_AL << + SDR_CTRLGRP_DRAMTIMING1_TAL_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TCL << + SDR_CTRLGRP_DRAMTIMING1_TCL_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TRRD << + SDR_CTRLGRP_DRAMTIMING1_TRRD_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TFAW << + SDR_CTRLGRP_DRAMTIMING1_TFAW_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING1_TRFC << + SDR_CTRLGRP_DRAMTIMING1_TRFC_LSB), + .dram_timing2 = + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TREFI << + SDR_CTRLGRP_DRAMTIMING2_TREFI_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TRCD << + SDR_CTRLGRP_DRAMTIMING2_TRCD_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TRP << + SDR_CTRLGRP_DRAMTIMING2_TRP_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TWR << + SDR_CTRLGRP_DRAMTIMING2_TWR_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING2_IF_TWTR << + SDR_CTRLGRP_DRAMTIMING2_TWTR_LSB), + .dram_timing3 = + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRTP << + SDR_CTRLGRP_DRAMTIMING3_TRTP_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRAS << + SDR_CTRLGRP_DRAMTIMING3_TRAS_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TRC << + SDR_CTRLGRP_DRAMTIMING3_TRC_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TMRD << + SDR_CTRLGRP_DRAMTIMING3_TMRD_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING3_TCCD << + SDR_CTRLGRP_DRAMTIMING3_TCCD_LSB), + .dram_timing4 = + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING4_SELFRFSHEXIT << + SDR_CTRLGRP_DRAMTIMING4_SELFRFSHEXIT_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMTIMING4_PWRDOWNEXIT << + SDR_CTRLGRP_DRAMTIMING4_PWRDOWNEXIT_LSB), + .lowpwr_timing = + (CONFIG_HPS_SDR_CTRLCFG_LOWPWRTIMING_AUTOPDCYCLES << + SDR_CTRLGRP_LOWPWRTIMING_AUTOPDCYCLES_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_LOWPWRTIMING_CLKDISABLECYCLES << + SDR_CTRLGRP_LOWPWRTIMING_CLKDISABLECYCLES_LSB), + .dram_odt = + (CONFIG_HPS_SDR_CTRLCFG_DRAMODT_READ << + SDR_CTRLGRP_DRAMODT_READ_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMODT_WRITE << + SDR_CTRLGRP_DRAMODT_WRITE_LSB), + .dram_addrw = + (CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_COLBITS << + SDR_CTRLGRP_DRAMADDRW_COLBITS_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS << + SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_BANKBITS << + SDR_CTRLGRP_DRAMADDRW_BANKBITS_LSB) | + ((CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_CSBITS - 1) << + SDR_CTRLGRP_DRAMADDRW_CSBITS_LSB), + .dram_if_width = + (CONFIG_HPS_SDR_CTRLCFG_DRAMIFWIDTH_IFWIDTH << + SDR_CTRLGRP_DRAMIFWIDTH_IFWIDTH_LSB), + .dram_dev_width = + (CONFIG_HPS_SDR_CTRLCFG_DRAMDEVWIDTH_DEVWIDTH << + SDR_CTRLGRP_DRAMDEVWIDTH_DEVWIDTH_LSB), + .dram_intr = + (CONFIG_HPS_SDR_CTRLCFG_DRAMINTR_INTREN << + SDR_CTRLGRP_DRAMINTR_INTREN_LSB), + .lowpwr_eq = + (CONFIG_HPS_SDR_CTRLCFG_LOWPWREQ_SELFRFSHMASK << + SDR_CTRLGRP_LOWPWREQ_SELFRFSHMASK_LSB), + .static_cfg = + (CONFIG_HPS_SDR_CTRLCFG_STATICCFG_MEMBL << + SDR_CTRLGRP_STATICCFG_MEMBL_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_STATICCFG_USEECCASDATA << + SDR_CTRLGRP_STATICCFG_USEECCASDATA_LSB), + .ctrl_width = + (CONFIG_HPS_SDR_CTRLCFG_CTRLWIDTH_CTRLWIDTH << + SDR_CTRLGRP_CTRLWIDTH_CTRLWIDTH_LSB), + .cport_width = + (CONFIG_HPS_SDR_CTRLCFG_CPORTWIDTH_CPORTWIDTH << + SDR_CTRLGRP_CPORTWIDTH_CMDPORTWIDTH_LSB), + .cport_wmap = + (CONFIG_HPS_SDR_CTRLCFG_CPORTWMAP_CPORTWMAP << + SDR_CTRLGRP_CPORTWMAP_CPORTWFIFOMAP_LSB), + .cport_rmap = + (CONFIG_HPS_SDR_CTRLCFG_CPORTRMAP_CPORTRMAP << + SDR_CTRLGRP_CPORTRMAP_CPORTRFIFOMAP_LSB), + .rfifo_cmap = + (CONFIG_HPS_SDR_CTRLCFG_RFIFOCMAP_RFIFOCMAP << + SDR_CTRLGRP_RFIFOCMAP_RFIFOCPORTMAP_LSB), + .wfifo_cmap = + (CONFIG_HPS_SDR_CTRLCFG_WFIFOCMAP_WFIFOCMAP << + SDR_CTRLGRP_WFIFOCMAP_WFIFOCPORTMAP_LSB), + .cport_rdwr = + (CONFIG_HPS_SDR_CTRLCFG_CPORTRDWR_CPORTRDWR << + SDR_CTRLGRP_CPORTRDWR_CPORTRDWR_LSB), + .port_cfg = + (CONFIG_HPS_SDR_CTRLCFG_PORTCFG_AUTOPCHEN << + SDR_CTRLGRP_PORTCFG_AUTOPCHEN_LSB), + .fpgaport_rst = CONFIG_HPS_SDR_CTRLCFG_FPGAPORTRST, + .fifo_cfg = + (CONFIG_HPS_SDR_CTRLCFG_FIFOCFG_SYNCMODE << + SDR_CTRLGRP_FIFOCFG_SYNCMODE_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_FIFOCFG_INCSYNC << + SDR_CTRLGRP_FIFOCFG_INCSYNC_LSB), + .mp_priority = + (CONFIG_HPS_SDR_CTRLCFG_MPPRIORITY_USERPRIORITY << + SDR_CTRLGRP_MPPRIORITY_USERPRIORITY_LSB), + .mp_weight0 = + (CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_0_STATICWEIGHT_31_0 << + SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_0_STATICWEIGHT_31_0_LSB), + .mp_weight1 = + (CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_1_STATICWEIGHT_49_32 << + SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_1_STATICWEIGHT_49_32_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_1_SUMOFWEIGHT_13_0 << + SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_1_SUMOFWEIGHTS_13_0_LSB), + .mp_weight2 = + (CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_2_SUMOFWEIGHT_45_14 << + SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_2_SUMOFWEIGHTS_45_14_LSB), + .mp_weight3 = + (CONFIG_HPS_SDR_CTRLCFG_MPWIEIGHT_3_SUMOFWEIGHT_63_46 << + SDR_CTRLGRP_MPWEIGHT_MPWEIGHT_3_SUMOFWEIGHTS_63_46_LSB), + .mp_pacing0 = + (CONFIG_HPS_SDR_CTRLCFG_MPPACING_0_THRESHOLD1_31_0 << + SDR_CTRLGRP_MPPACING_MPPACING_0_THRESHOLD1_31_0_LSB), + .mp_pacing1 = + (CONFIG_HPS_SDR_CTRLCFG_MPPACING_1_THRESHOLD1_59_32 << + SDR_CTRLGRP_MPPACING_MPPACING_1_THRESHOLD1_59_32_LSB) | + (CONFIG_HPS_SDR_CTRLCFG_MPPACING_1_THRESHOLD2_3_0 << + SDR_CTRLGRP_MPPACING_MPPACING_1_THRESHOLD2_3_0_LSB), + .mp_pacing2 = + (CONFIG_HPS_SDR_CTRLCFG_MPPACING_2_THRESHOLD2_35_4 << + SDR_CTRLGRP_MPPACING_MPPACING_2_THRESHOLD2_35_4_LSB), + .mp_pacing3 = + (CONFIG_HPS_SDR_CTRLCFG_MPPACING_3_THRESHOLD2_59_36 << + SDR_CTRLGRP_MPPACING_MPPACING_3_THRESHOLD2_59_36_LSB), + .mp_threshold0 = + (CONFIG_HPS_SDR_CTRLCFG_MPTHRESHOLDRST_0_THRESHOLDRSTCYCLES_31_0 << + SDR_CTRLGRP_MPTHRESHOLDRST_0_THRESHOLDRSTCYCLES_31_0_LSB), + .mp_threshold1 = + (CONFIG_HPS_SDR_CTRLCFG_MPTHRESHOLDRST_1_THRESHOLDRSTCYCLES_63_32 << + SDR_CTRLGRP_MPTHRESHOLDRST_1_THRESHOLDRSTCYCLES_63_32_LSB), + .mp_threshold2 = + (CONFIG_HPS_SDR_CTRLCFG_MPTHRESHOLDRST_2_THRESHOLDRSTCYCLES_79_64 << + SDR_CTRLGRP_MPTHRESHOLDRST_2_THRESHOLDRSTCYCLES_79_64_LSB), + .phy_ctrl0 = CONFIG_HPS_SDR_CTRLCFG_PHYCTRL_PHYCTRL_0, +}; + +static const struct socfpga_sdram_rw_mgr_config rw_mgr_config = { + .activate_0_and_1 = RW_MGR_ACTIVATE_0_AND_1, + .activate_0_and_1_wait1 = RW_MGR_ACTIVATE_0_AND_1_WAIT1, + .activate_0_and_1_wait2 = RW_MGR_ACTIVATE_0_AND_1_WAIT2, + .activate_1 = RW_MGR_ACTIVATE_1, + .clear_dqs_enable = RW_MGR_CLEAR_DQS_ENABLE, + .guaranteed_read = RW_MGR_GUARANTEED_READ, + .guaranteed_read_cont = RW_MGR_GUARANTEED_READ_CONT, + .guaranteed_write = RW_MGR_GUARANTEED_WRITE, + .guaranteed_write_wait0 = RW_MGR_GUARANTEED_WRITE_WAIT0, + .guaranteed_write_wait1 = RW_MGR_GUARANTEED_WRITE_WAIT1, + .guaranteed_write_wait2 = RW_MGR_GUARANTEED_WRITE_WAIT2, + .guaranteed_write_wait3 = RW_MGR_GUARANTEED_WRITE_WAIT3, + .idle = RW_MGR_IDLE, + .idle_loop1 = RW_MGR_IDLE_LOOP1, + .idle_loop2 = RW_MGR_IDLE_LOOP2, + .init_reset_0_cke_0 = RW_MGR_INIT_RESET_0_CKE_0, + .init_reset_1_cke_0 = RW_MGR_INIT_RESET_1_CKE_0, + .lfsr_wr_rd_bank_0 = RW_MGR_LFSR_WR_RD_BANK_0, + .lfsr_wr_rd_bank_0_data = RW_MGR_LFSR_WR_RD_BANK_0_DATA, + .lfsr_wr_rd_bank_0_dqs = RW_MGR_LFSR_WR_RD_BANK_0_DQS, + .lfsr_wr_rd_bank_0_nop = RW_MGR_LFSR_WR_RD_BANK_0_NOP, + .lfsr_wr_rd_bank_0_wait = RW_MGR_LFSR_WR_RD_BANK_0_WAIT, + .lfsr_wr_rd_bank_0_wl_1 = RW_MGR_LFSR_WR_RD_BANK_0_WL_1, + .lfsr_wr_rd_dm_bank_0 = RW_MGR_LFSR_WR_RD_DM_BANK_0, + .lfsr_wr_rd_dm_bank_0_data = RW_MGR_LFSR_WR_RD_DM_BANK_0_DATA, + .lfsr_wr_rd_dm_bank_0_dqs = RW_MGR_LFSR_WR_RD_DM_BANK_0_DQS, + .lfsr_wr_rd_dm_bank_0_nop = RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP, + .lfsr_wr_rd_dm_bank_0_wait = RW_MGR_LFSR_WR_RD_DM_BANK_0_WAIT, + .lfsr_wr_rd_dm_bank_0_wl_1 = RW_MGR_LFSR_WR_RD_DM_BANK_0_WL_1, + .mrs0_dll_reset = RW_MGR_MRS0_DLL_RESET, + .mrs0_dll_reset_mirr = RW_MGR_MRS0_DLL_RESET_MIRR, + .mrs0_user = RW_MGR_MRS0_USER, + .mrs0_user_mirr = RW_MGR_MRS0_USER_MIRR, + .mrs1 = RW_MGR_MRS1, + .mrs1_mirr = RW_MGR_MRS1_MIRR, + .mrs2 = RW_MGR_MRS2, + .mrs2_mirr = RW_MGR_MRS2_MIRR, + .mrs3 = RW_MGR_MRS3, + .mrs3_mirr = RW_MGR_MRS3_MIRR, + .precharge_all = RW_MGR_PRECHARGE_ALL, + .read_b2b = RW_MGR_READ_B2B, + .read_b2b_wait1 = RW_MGR_READ_B2B_WAIT1, + .read_b2b_wait2 = RW_MGR_READ_B2B_WAIT2, + .refresh_all = RW_MGR_REFRESH_ALL, + .rreturn = RW_MGR_RETURN, + .sgle_read = RW_MGR_SGLE_READ, + .zqcl = RW_MGR_ZQCL, + + .true_mem_data_mask_width = RW_MGR_TRUE_MEM_DATA_MASK_WIDTH, + .mem_address_mirroring = RW_MGR_MEM_ADDRESS_MIRRORING, + .mem_data_mask_width = RW_MGR_MEM_DATA_MASK_WIDTH, + .mem_data_width = RW_MGR_MEM_DATA_WIDTH, + .mem_dq_per_read_dqs = RW_MGR_MEM_DQ_PER_READ_DQS, + .mem_dq_per_write_dqs = RW_MGR_MEM_DQ_PER_WRITE_DQS, + .mem_if_read_dqs_width = RW_MGR_MEM_IF_READ_DQS_WIDTH, + .mem_if_write_dqs_width = RW_MGR_MEM_IF_WRITE_DQS_WIDTH, + .mem_number_of_cs_per_dimm = RW_MGR_MEM_NUMBER_OF_CS_PER_DIMM, + .mem_number_of_ranks = RW_MGR_MEM_NUMBER_OF_RANKS, + .mem_virtual_groups_per_read_dqs = + RW_MGR_MEM_VIRTUAL_GROUPS_PER_READ_DQS, + .mem_virtual_groups_per_write_dqs = + RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS, +}; + +struct socfpga_sdram_io_config io_config = { + .delay_per_dchain_tap = IO_DELAY_PER_DCHAIN_TAP, + .delay_per_dqs_en_dchain_tap = IO_DELAY_PER_DQS_EN_DCHAIN_TAP, + .delay_per_opa_tap = IO_DELAY_PER_OPA_TAP, + .dll_chain_length = IO_DLL_CHAIN_LENGTH, + .dqdqs_out_phase_max = IO_DQDQS_OUT_PHASE_MAX, + .dqs_en_delay_max = IO_DQS_EN_DELAY_MAX, + .dqs_en_delay_offset = IO_DQS_EN_DELAY_OFFSET, + .dqs_en_phase_max = IO_DQS_EN_PHASE_MAX, + .dqs_in_delay_max = IO_DQS_IN_DELAY_MAX, + .dqs_in_reserve = IO_DQS_IN_RESERVE, + .dqs_out_reserve = IO_DQS_OUT_RESERVE, + .io_in_delay_max = IO_IO_IN_DELAY_MAX, + .io_out1_delay_max = IO_IO_OUT1_DELAY_MAX, + .io_out2_delay_max = IO_IO_OUT2_DELAY_MAX, + .shift_dqs_en_when_shift_dqs = IO_SHIFT_DQS_EN_WHEN_SHIFT_DQS, +}; + +struct socfpga_sdram_misc_config misc_config = { + .afi_rate_ratio = AFI_RATE_RATIO, + .calib_lfifo_offset = CALIB_LFIFO_OFFSET, + .calib_vfifo_offset = CALIB_VFIFO_OFFSET, + .enable_super_quick_calibration = ENABLE_SUPER_QUICK_CALIBRATION, + .max_latency_count_width = MAX_LATENCY_COUNT_WIDTH, + .read_valid_fifo_size = READ_VALID_FIFO_SIZE, + .reg_file_init_seq_signature = REG_FILE_INIT_SEQ_SIGNATURE, + .tinit_cntr0_val = TINIT_CNTR0_VAL, + .tinit_cntr1_val = TINIT_CNTR1_VAL, + .tinit_cntr2_val = TINIT_CNTR2_VAL, + .treset_cntr0_val = TRESET_CNTR0_VAL, + .treset_cntr1_val = TRESET_CNTR1_VAL, + .treset_cntr2_val = TRESET_CNTR2_VAL, +}; + +const struct socfpga_sdram_config *socfpga_get_sdram_config(void) +{ + return &sdram_config; +} + +void socfpga_get_seq_ac_init(const u32 **init, unsigned int *nelem) +{ + *init = ac_rom_init; + *nelem = ARRAY_SIZE(ac_rom_init); +} + +void socfpga_get_seq_inst_init(const u32 **init, unsigned int *nelem) +{ + *init = inst_rom_init; + *nelem = ARRAY_SIZE(inst_rom_init); +} + +const struct socfpga_sdram_rw_mgr_config *socfpga_get_sdram_rwmgr_config(void) +{ + return &rw_mgr_config; +} + +const struct socfpga_sdram_io_config *socfpga_get_sdram_io_config(void) +{ + return &io_config; +} + +const struct socfpga_sdram_misc_config *socfpga_get_sdram_misc_config(void) +{ + return &misc_config; +} diff --git a/configs/socfpga_arria5_defconfig b/configs/socfpga_arria5_defconfig index 4ba4b8c5f26..4d1cd21c151 100644 --- a/configs/socfpga_arria5_defconfig +++ b/configs/socfpga_arria5_defconfig @@ -7,3 +7,13 @@ CONFIG_SPL=y # CONFIG_CMD_FLASH is not set CONFIG_OF_CONTROL=y CONFIG_SPI_FLASH=y +CONFIG_SPL_DM=y +CONFIG_SPL_MMC_SUPPORT=y +CONFIG_DM_SEQ_ALIAS=y +CONFIG_SPL_SIMPLE_BUS=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPL_SPI_SUPPORT=y +CONFIG_SPL_STACK_R=y +CONFIG_SPL_STACK_R_ADDR=0x00800000 +CONFIG_SYS_MALLOC_F_LEN=0x2000 diff --git a/configs/socfpga_cyclone5_defconfig b/configs/socfpga_cyclone5_defconfig index e101f767122..ae3a1dea911 100644 --- a/configs/socfpga_cyclone5_defconfig +++ b/configs/socfpga_cyclone5_defconfig @@ -7,5 +7,16 @@ CONFIG_SPL=y # CONFIG_CMD_FLASH is not set CONFIG_OF_CONTROL=y CONFIG_SPI_FLASH=y +CONFIG_DM_ETH=y CONFIG_NETDEVICES=y CONFIG_ETH_DESIGNWARE=y +CONFIG_SPL_DM=y +CONFIG_SPL_MMC_SUPPORT=y +CONFIG_DM_SEQ_ALIAS=y +CONFIG_SPL_SIMPLE_BUS=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPL_SPI_SUPPORT=y +CONFIG_SPL_STACK_R=y +CONFIG_SPL_STACK_R_ADDR=0x00800000 +CONFIG_SYS_MALLOC_F_LEN=0x2000 diff --git a/configs/socfpga_socrates_defconfig b/configs/socfpga_socrates_defconfig index 63dda73dd21..71d47116791 100644 --- a/configs/socfpga_socrates_defconfig +++ b/configs/socfpga_socrates_defconfig @@ -6,7 +6,17 @@ CONFIG_SPL=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set CONFIG_OF_CONTROL=y -CONFIG_SPL_DISABLE_OF_CONTROL=y CONFIG_SPI_FLASH=y +CONFIG_DM_ETH=y CONFIG_NETDEVICES=y CONFIG_ETH_DESIGNWARE=y +CONFIG_SPL_DM=y +CONFIG_SPL_MMC_SUPPORT=y +CONFIG_DM_SEQ_ALIAS=y +CONFIG_SPL_SIMPLE_BUS=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPL_SPI_SUPPORT=y +CONFIG_SPL_STACK_R=y +CONFIG_SPL_STACK_R_ADDR=0x00800000 +CONFIG_SYS_MALLOC_F_LEN=0x2000 diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile new file mode 100644 index 00000000000..1ca705856d2 --- /dev/null +++ b/drivers/ddr/altera/Makefile @@ -0,0 +1,11 @@ +# +# (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> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_ALTERA_SDRAM) += sdram.o sequencer.o diff --git a/drivers/ddr/altera/sdram.c b/drivers/ddr/altera/sdram.c new file mode 100644 index 00000000000..1ed2883d1b8 --- /dev/null +++ b/drivers/ddr/altera/sdram.c @@ -0,0 +1,535 @@ +/* + * Copyright Altera Corporation (C) 2014-2015 + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <errno.h> +#include <div64.h> +#include <watchdog.h> +#include <asm/arch/fpga_manager.h> +#include <asm/arch/sdram.h> +#include <asm/arch/system_manager.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +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 struct socfpga_system_manager *sysmgr_regs = + (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; +static struct socfpga_sdr_ctrl *sdr_ctrl = + (struct socfpga_sdr_ctrl *)SDR_CTRLGRP_ADDRESS; + +/** + * 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 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 >> 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 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(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(&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(&rule); + + /* default rule: reject everything */ + writel(0x3ff, &sdr_ctrl->protport_default); +} + +static void sdram_dump_protection_config(void) +{ + 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++) { + sdram_get_rule(&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(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); +} + +/** + * 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(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, &sysmgr_regs->iswgrp_handoff[4]); + + sdr_load_regs(cfg); + + /* saving this value to SYSMGR.ISWGRP.HANDOFF.FPGA2SDR */ + writel(cfg->fpgaport_rst, &sysmgr_regs->iswgrp_handoff[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(0, sdram_calculate_size() - 1); + + sdram_dump_protection_config(); + + return 0; +} + +/** + * sdram_calculate_size() - Calculate SDRAM size + * + * Calculate SDRAM device size based on SDRAM controller parameters. + * Size is specified in bytes. + */ +unsigned long sdram_calculate_size(void) +{ + 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(&sysmgr_regs->iswgrp_handoff[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; +} diff --git a/drivers/ddr/altera/sequencer.c b/drivers/ddr/altera/sequencer.c new file mode 100644 index 00000000000..2bd01092eed --- /dev/null +++ b/drivers/ddr/altera/sequencer.c @@ -0,0 +1,3783 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/sdram.h> +#include <errno.h> +#include "sequencer.h" + +static struct socfpga_sdr_rw_load_manager *sdr_rw_load_mgr_regs = + (struct socfpga_sdr_rw_load_manager *) + (SDR_PHYGRP_RWMGRGRP_ADDRESS | 0x800); +static 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 struct socfpga_sdr_reg_file *sdr_reg_file = + (struct socfpga_sdr_reg_file *)SDR_PHYGRP_REGFILEGRP_ADDRESS; +static struct socfpga_sdr_scc_mgr *sdr_scc_mgr = + (struct socfpga_sdr_scc_mgr *) + (SDR_PHYGRP_SCCGRP_ADDRESS | 0xe00); +static struct socfpga_phy_mgr_cmd *phy_mgr_cmd = + (struct socfpga_phy_mgr_cmd *)SDR_PHYGRP_PHYMGRGRP_ADDRESS; +static struct socfpga_phy_mgr_cfg *phy_mgr_cfg = + (struct socfpga_phy_mgr_cfg *) + (SDR_PHYGRP_PHYMGRGRP_ADDRESS | 0x40); +static struct socfpga_data_mgr *data_mgr = + (struct socfpga_data_mgr *)SDR_PHYGRP_DATAMGRGRP_ADDRESS; +static struct socfpga_sdr_ctrl *sdr_ctrl = + (struct socfpga_sdr_ctrl *)SDR_CTRLGRP_ADDRESS; + +const struct socfpga_sdram_rw_mgr_config *rwcfg; +const struct socfpga_sdram_io_config *iocfg; +const struct socfpga_sdram_misc_config *misccfg; + +#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) + +/* 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 */ + +#define SKIP_DELAY_LOOP_VALUE_OR_ZERO(non_skip_value) \ + ((non_skip_value) & skip_delay_mask) + +struct gbl_type *gbl; +struct param_type *param; + +static void set_failing_group_stage(u32 group, u32 stage, + u32 substage) +{ + /* + * Only set the global stage if there was not been any other + * failing group + */ + if (gbl->error_stage == CAL_STAGE_NIL) { + gbl->error_substage = substage; + gbl->error_stage = stage; + 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(void) +{ + 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 ((dyn_calib_steps & CALIB_SKIP_ALL) == CALIB_SKIP_ALL) + return; + + ratio = rwcfg->mem_dq_per_read_dqs / + rwcfg->mem_virtual_groups_per_read_dqs; + param->read_correct_mask_vg = (1 << ratio) - 1; + param->write_correct_mask_vg = (1 << ratio) - 1; + param->read_correct_mask = (1 << rwcfg->mem_dq_per_read_dqs) - 1; + param->write_correct_mask = (1 << 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(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 (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 (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; + } 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 */ + /* Read: + * ----------+-----------------------+ + * | ODT | + * Read From +-----------------------+ + * 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 | + * ----------+-----+-----+-----+-----+ + * + * 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; + odt_mask_1 = 0x5; + break; + case 1: + odt_mask_0 = 0x8; + odt_mask_1 = 0xA; + break; + case 2: + odt_mask_0 = 0x1; + odt_mask_1 = 0x5; + break; + case 3: + odt_mask_0 = 0x2; + 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, 0, i); + } +} + +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_dqs_io_in_delay(u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_IN_DELAY_OFFSET, rwcfg->mem_dq_per_write_dqs, + 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_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(u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_OUT1_DELAY_OFFSET, rwcfg->mem_dq_per_write_dqs, + delay); +} + +static void scc_mgr_set_dm_out1_delay(u32 dm, u32 delay) +{ + scc_mgr_set(SCC_MGR_IO_OUT1_DELAY_OFFSET, + 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(const u32 off, const u32 grp, const u32 val, + const int update) +{ + u32 r; + + for (r = 0; r < 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(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(SCC_MGR_DQS_EN_PHASE_OFFSET, + read_group, phase, 0); +} + +static void scc_mgr_set_dqdqs_output_phase_all_ranks(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(SCC_MGR_DQDQS_OUT_PHASE_OFFSET, + write_group, phase, 0); +} + +static void scc_mgr_set_dqs_en_delay_all_ranks(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(SCC_MGR_DQS_EN_DELAY_OFFSET, + read_group, delay, 1); + writel(0, &sdr_scc_mgr->update); +} + +/** + * 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(const u32 write_group, const u32 delay) +{ + const int ratio = rwcfg->mem_if_read_dqs_width / + 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(void) +{ + int i, r; + + /* + * USER Zero all DQS config settings, across all groups and all + * shadow registers + */ + for (r = 0; r < rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + for (i = 0; i < 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, iocfg->dqs_in_reserve); + scc_mgr_set_dqs_en_phase(i, 0); + scc_mgr_set_dqs_en_delay(i, 0); + } + + for (i = 0; i < 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(i, 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(const u32 write_group) +{ + const int ratio = rwcfg->mem_if_read_dqs_width / + 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(const u32 write_group, const int out_only) +{ + int i, r; + + for (r = 0; r < rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + /* Zero all DQ config settings. */ + for (i = 0; i < 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++) + scc_mgr_set_dm_out1_delay(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(0); + + /* Arria V/Cyclone V don't have out2. */ + scc_mgr_set_dqs_out1_delay(iocfg->dqs_out_reserve); + scc_mgr_set_oct_out1_delay(write_group, iocfg->dqs_out_reserve); + scc_mgr_load_dqs_for_write_group(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(u32 group_bgn, u32 delay) +{ + u32 i, p; + + for (i = 0, p = group_bgn; i < 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(const u32 delay) +{ + int i; + + for (i = 0; i < 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(u32 delay1) +{ + u32 i; + + for (i = 0; i < RW_MGR_NUM_DM_PER_WRITE_GROUP; i++) { + scc_mgr_set_dm_out1_delay(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(u32 write_group, + u32 delay) +{ + scc_mgr_set_dqs_out1_delay(delay); + scc_mgr_load_dqs_io(); + + scc_mgr_set_oct_out1_delay(write_group, delay); + scc_mgr_load_dqs_for_write_group(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(const u32 write_group, + const u32 delay) +{ + u32 i, new_delay; + + /* DQ shift */ + for (i = 0; i < 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 > 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, + iocfg->io_out2_delay_max, + new_delay - iocfg->io_out2_delay_max); + new_delay -= iocfg->io_out2_delay_max; + scc_mgr_set_dqs_out1_delay(new_delay); + } + + scc_mgr_load_dqs_io(); + + /* OCT shift */ + new_delay = READ_SCC_OCT_OUT2_DELAY + delay; + if (new_delay > 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, iocfg->io_out2_delay_max, + new_delay - iocfg->io_out2_delay_max); + new_delay -= iocfg->io_out2_delay_max; + scc_mgr_set_oct_out1_delay(write_group, new_delay); + } + + scc_mgr_load_dqs_for_write_group(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(const u32 write_group, + const u32 delay) +{ + int r; + + for (r = 0; r < rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + scc_mgr_apply_group_all_out_delay_add(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(void) +{ + /* + * 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(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(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, 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(rwcfg->idle_loop1, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + writel(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(rwcfg->idle_loop2, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(rwcfg->idle_loop2, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + do { + writel(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); +} + +/** + * 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(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() - 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(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 < rwcfg->mem_number_of_ranks; r++) { + /* set rank */ + set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_OFF); + + /* precharge all banks ... */ + if (precharge) + writel(rwcfg->precharge_all, grpaddr); + + /* + * USER Use Mirror-ed commands for odd ranks if address + * mirrorring is on + */ + if ((rwcfg->mem_address_mirroring >> r) & 0x1) { + set_jump_as_return(); + writel(rwcfg->mrs2_mirr, grpaddr); + delay_for_n_mem_clocks(4); + set_jump_as_return(); + writel(rwcfg->mrs3_mirr, grpaddr); + delay_for_n_mem_clocks(4); + set_jump_as_return(); + writel(rwcfg->mrs1_mirr, grpaddr); + delay_for_n_mem_clocks(4); + set_jump_as_return(); + writel(fin1, grpaddr); + } else { + set_jump_as_return(); + writel(rwcfg->mrs2, grpaddr); + delay_for_n_mem_clocks(4); + set_jump_as_return(); + writel(rwcfg->mrs3, grpaddr); + delay_for_n_mem_clocks(4); + set_jump_as_return(); + writel(rwcfg->mrs1, grpaddr); + set_jump_as_return(); + writel(fin2, grpaddr); + } + + if (precharge) + continue; + + set_jump_as_return(); + writel(rwcfg->zqcl, grpaddr); + + /* tZQinit = tDLLK = 512 ck cycles */ + delay_for_n_mem_clocks(512); + } +} + +/** + * rw_mgr_mem_initialize() - Initialize RW Manager + * + * Initialize RW Manager. + */ +static void rw_mgr_mem_initialize(void) +{ + debug("%s:%d\n", __func__, __LINE__); + + /* The reset / cke part of initialization is broadcasted to all ranks */ + 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(misccfg->tinit_cntr0_val, + misccfg->tinit_cntr1_val, + misccfg->tinit_cntr2_val, + rwcfg->init_reset_0_cke_0); + + /* Indicate that memory is stable. */ + writel(1, &phy_mgr_cfg->reset_mem_stbl); + + /* + * 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(misccfg->treset_cntr0_val, + misccfg->treset_cntr1_val, + misccfg->treset_cntr2_val, + rwcfg->init_reset_1_cke_0); + + /* Bring up clock enable. */ + + /* tXRP < 250 ck cycles */ + delay_for_n_mem_clocks(250); + + rw_mgr_mem_load_user(rwcfg->mrs0_dll_reset_mirr, 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(void) +{ + rw_mgr_mem_load_user(rwcfg->mrs0_user_mirr, 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(u32 group, + u32 test_dm) +{ + const u32 quick_write_mode = + (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES) && + 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 = 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 = rwcfg->lfsr_wr_rd_dm_bank_0_wl_1; + writel(rwcfg->lfsr_wr_rd_dm_bank_0_data, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + writel(rwcfg->lfsr_wr_rd_dm_bank_0_nop, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + } else { + mcc_instruction = rwcfg->lfsr_wr_rd_bank_0_wl_1; + writel(rwcfg->lfsr_wr_rd_bank_0_data, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + writel(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 = rwcfg->lfsr_wr_rd_dm_bank_0; + writel(rwcfg->lfsr_wr_rd_dm_bank_0_dqs, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + } else { + mcc_instruction = rwcfg->lfsr_wr_rd_bank_0; + writel(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 = rwcfg->lfsr_wr_rd_dm_bank_0; + writel(rwcfg->lfsr_wr_rd_dm_bank_0_nop, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + } else { + mcc_instruction = rwcfg->lfsr_wr_rd_bank_0; + writel(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(rwcfg->lfsr_wr_rd_dm_bank_0_wait, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + } else { + writel(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(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 ? + rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + const u32 shift_ratio = rwcfg->mem_dq_per_write_dqs / + rwcfg->mem_virtual_groups_per_write_dqs; + const u32 correct_mask_vg = param->write_correct_mask_vg; + + u32 tmp_bit_chk, base_rw_mgr; + int vg, r; + + *bit_chk = param->write_correct_mask; + + for (r = rank_bgn; r < rank_end; r++) { + /* Set rank */ + set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_READ_WRITE); + + tmp_bit_chk = 0; + for (vg = 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); + + rw_mgr_mem_calibrate_write_test_issue( + write_group * + rwcfg->mem_virtual_groups_per_write_dqs + vg, + 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(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, + param->write_correct_mask, + *bit_chk == param->write_correct_mask); + return *bit_chk == param->write_correct_mask; + } else { + set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); + 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(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 * rwcfg->mem_virtual_groups_per_read_dqs) << 2; + const u32 rank_end = all_ranks ? + rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + const u32 shift_ratio = rwcfg->mem_dq_per_read_dqs / + rwcfg->mem_virtual_groups_per_read_dqs; + const u32 correct_mask_vg = param->read_correct_mask_vg; + + u32 tmp_bit_chk, base_rw_mgr, bit_chk; + int vg, r; + int ret = 0; + + bit_chk = param->read_correct_mask; + + for (r = rank_bgn; r < rank_end; r++) { + /* Set rank */ + set_rank_and_odt_mask(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(rwcfg->guaranteed_read, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr1); + writel(rwcfg->guaranteed_read_cont, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + tmp_bit_chk = 0; + for (vg = 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(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(rwcfg->clear_dqs_enable, addr + (group << 2)); + + set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); + + if (bit_chk != 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, + 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(const u32 rank_bgn, + const int all_ranks) +{ + const u32 rank_end = all_ranks ? + 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(r, RW_MGR_ODT_MODE_READ_WRITE); + + /* Load up a constant bursts */ + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr0); + + writel(rwcfg->guaranteed_write_wait0, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + + writel(0x20, &sdr_rw_load_mgr_regs->load_cntr1); + + writel(rwcfg->guaranteed_write_wait1, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + writel(0x04, &sdr_rw_load_mgr_regs->load_cntr2); + + writel(rwcfg->guaranteed_write_wait2, + &sdr_rw_load_jump_mgr_regs->load_jump_add2); + + writel(0x04, &sdr_rw_load_mgr_regs->load_cntr3); + + writel(rwcfg->guaranteed_write_wait3, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + + writel(rwcfg->guaranteed_write, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + } + + set_rank_and_odt_mask(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(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 ? rwcfg->mem_number_of_ranks : + (rank_bgn + NUM_RANKS_PER_SHADOW_REG); + const u32 quick_read_mode = + ((STATIC_CALIB_STEPS & CALIB_SKIP_DELAY_SWEEPS) && + misccfg->enable_super_quick_calibration); + u32 correct_mask_vg = param->read_correct_mask_vg; + u32 tmp_bit_chk; + u32 base_rw_mgr; + u32 addr; + + int r, vg, ret; + + *bit_chk = param->read_correct_mask; + + for (r = rank_bgn; r < rank_end; r++) { + /* set rank */ + set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_READ_WRITE); + + writel(0x10, &sdr_rw_load_mgr_regs->load_cntr1); + + writel(rwcfg->read_b2b_wait1, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + writel(0x10, &sdr_rw_load_mgr_regs->load_cntr2); + writel(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(rwcfg->read_b2b, + &sdr_rw_load_jump_mgr_regs->load_jump_add0); + if (all_groups) + writel(rwcfg->mem_if_read_dqs_width * + 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(rwcfg->read_b2b, + &sdr_rw_load_jump_mgr_regs->load_jump_add3); + + tmp_bit_chk = 0; + for (vg = 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(rwcfg->read_b2b, addr + + ((group * + rwcfg->mem_virtual_groups_per_read_dqs + + vg) << 2)); + + base_rw_mgr = readl(SDR_PHYGRP_RWMGRGRP_ADDRESS); + tmp_bit_chk <<= rwcfg->mem_dq_per_read_dqs / + 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(rwcfg->clear_dqs_enable, addr + (group << 2)); + + set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF); + + if (all_correct) { + ret = (*bit_chk == 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, + 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(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(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(const u32 grp) +{ + u32 i; + + for (i = 0; i < 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(const u32 grp) +{ + u32 v, ret, fail_cnt = 0; + + for (v = 0; v < 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(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(int working, int delay, const u32 grp, + u32 *work, const u32 work_inc, u32 *pd) +{ + const u32 max = delay ? iocfg->dqs_en_delay_max : + iocfg->dqs_en_phase_max; + u32 ret; + + for (; *pd <= max; (*pd)++) { + if (delay) + scc_mgr_set_dqs_en_delay_all_ranks(grp, *pd); + else + scc_mgr_set_dqs_en_phase_all_ranks(grp, *pd); + + ret = rw_mgr_mem_calibrate_read_test_all_ranks(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(int working, const u32 grp, u32 *work, + u32 *i, u32 *p) +{ + const u32 end = misccfg->read_valid_fifo_size + (working ? 0 : 1); + int ret; + + for (; *i < end; (*i)++) { + if (working) + *p = 0; + + ret = sdr_find_phase_delay(working, 0, grp, work, + iocfg->delay_per_opa_tap, p); + if (!ret) + return 0; + + if (*p > 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(const u32 grp, u32 *work_bgn, u32 *d, + u32 *p, u32 *i) +{ + const u32 dtaps_per_ptap = iocfg->delay_per_opa_tap / + 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(grp, *d); + ret = sdr_find_phase(1, grp, work_bgn, i, p); + if (!ret) + return 0; + *work_bgn += 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(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 = iocfg->dqs_en_phase_max; + rw_mgr_decr_vfifo(grp); + } else { + (*p)--; + } + tmp_delay = *work_bgn - iocfg->delay_per_opa_tap; + scc_mgr_set_dqs_en_phase_all_ranks(grp, *p); + + for (d = 0; d <= iocfg->dqs_en_delay_max && tmp_delay < *work_bgn; + d++) { + scc_mgr_set_dqs_en_delay_all_ranks(grp, d); + + ret = rw_mgr_mem_calibrate_read_test_all_ranks(grp, 1, + PASS_ONE_BIT, 0); + if (ret) { + *work_bgn = tmp_delay; + break; + } + + tmp_delay += iocfg->delay_per_dqs_en_dchain_tap; + } + + /* Restore VFIFO to old state before we decremented it (if needed). */ + (*p)++; + if (*p > iocfg->dqs_en_phase_max) { + *p = 0; + rw_mgr_incr_vfifo(grp); + } + + scc_mgr_set_dqs_en_delay_all_ranks(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(const u32 grp, u32 *work_end, u32 *p, u32 *i) +{ + int ret; + + (*p)++; + *work_end += iocfg->delay_per_opa_tap; + if (*p > iocfg->dqs_en_phase_max) { + /* Fiddle with FIFO. */ + *p = 0; + rw_mgr_incr_vfifo(grp); + } + + ret = sdr_find_phase(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(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 = (iocfg->dqs_en_phase_max + 1) * 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, iocfg->delay_per_opa_tap); + if (tmp_delay > iocfg->dqs_en_phase_max * iocfg->delay_per_opa_tap) + tmp_delay = iocfg->dqs_en_phase_max * iocfg->delay_per_opa_tap; + p = tmp_delay / 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, + iocfg->delay_per_dqs_en_dchain_tap); + if (d > iocfg->dqs_en_delay_max) + d = iocfg->dqs_en_delay_max; + tmp_delay += d * 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(grp, p); + scc_mgr_set_dqs_en_delay_all_ranks(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 < 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(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(const u32 grp) +{ + u32 d, p, i; + u32 dtaps_per_ptap; + u32 work_bgn, work_end; + u32 found_passing_read, found_failing_read, 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(grp, 0); + scc_mgr_set_dqs_en_phase_all_ranks(grp, 0); + + /* Step 0: Determine number of delay taps for each phase tap. */ + dtaps_per_ptap = iocfg->delay_per_opa_tap / + iocfg->delay_per_dqs_en_dchain_tap; + + /* Step 1: First push vfifo until we get a failing read. */ + find_vfifo_failing_read(grp); + + /* Step 2: Find first working phase, increment in ptaps. */ + work_bgn = 0; + ret = sdr_working_phase(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(grp, &work_bgn, &p); + + /* + * Step 4a: go forward from working phase to non working + * phase, increment in ptaps. + */ + ret = sdr_nonworking_phase(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 = iocfg->dqs_en_phase_max; + rw_mgr_decr_vfifo(grp); + } else { + p = p - 1; + } + + work_end -= iocfg->delay_per_opa_tap; + scc_mgr_set_dqs_en_phase_all_ranks(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(0, 1, grp, &work_end, + iocfg->delay_per_dqs_en_dchain_tap, &d); + + /* Go back to working dtap */ + if (d != 0) + work_end -= 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 = iocfg->dqs_en_phase_max; + rw_mgr_decr_vfifo(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(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(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(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 + * (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(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(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 = rwcfg->mem_if_read_dqs_width / + rwcfg->mem_if_write_dqs_width; + const u32 correct_mask = write ? param->write_correct_mask : + param->read_correct_mask; + const u32 per_dqs = write ? rwcfg->mem_dq_per_write_dqs : + 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(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(rank_bgn, read_group, + NUM_READ_PB_TESTS, + PASS_ONE_BIT, bit_chk, + 0, 0); + } else { /* READ-ONLY */ + rw_mgr_mem_calibrate_write_test(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(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 ? iocfg->io_out1_delay_max : + iocfg->io_in_delay_max; + const u32 dqs_max = write ? iocfg->io_out1_delay_max : + iocfg->dqs_in_delay_max; + const u32 per_dqs = write ? rwcfg->mem_dq_per_write_dqs : + 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(d); + else + scc_mgr_apply_group_dq_in_delay(test_bgn, d); + + writel(0, &sdr_scc_mgr->update); + + stop = search_stop_check(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(0); + else + scc_mgr_apply_group_dq_in_delay(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(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 ? iocfg->io_out1_delay_max : + iocfg->io_in_delay_max; + const u32 dqs_max = write ? iocfg->io_out1_delay_max : + iocfg->dqs_in_delay_max; + const u32 per_dqs = write ? rwcfg->mem_dq_per_write_dqs : + 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(write_group, + d + start_dqs); + } else { /* READ-ONLY */ + scc_mgr_set_dqs_bus_in_delay(read_group, d + start_dqs); + if (iocfg->shift_dqs_en_when_shift_dqs) { + u32 delay = d + start_dqs_en; + if (delay > iocfg->dqs_en_delay_max) + delay = 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(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 < 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(const int write, int *left_edge, + int *right_edge, int *mid_min) +{ + const u32 per_dqs = write ? rwcfg->mem_dq_per_write_dqs : + 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(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 u32 delay_max = write ? iocfg->io_out1_delay_max : + iocfg->io_in_delay_max; + const u32 per_dqs = write ? rwcfg->mem_dq_per_write_dqs : + rwcfg->mem_dq_per_read_dqs; + const u32 delay_off = write ? SCC_MGR_IO_OUT1_DELAY_OFFSET : + SCC_MGR_IO_IN_DELAY_OFFSET; + const u32 addr = SDR_PHYGRP_SCCGRP_ADDRESS | delay_off; + + u32 temp_dq_io_delay1, temp_dq_io_delay2; + 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 + (p << 2)); + temp_dq_io_delay2 = readl(addr + (i << 2)); + + if (shift_dq + temp_dq_io_delay1 > delay_max) + shift_dq = delay_max - temp_dq_io_delay2; + 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(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; + int32_t left_edge[rwcfg->mem_dq_per_read_dqs]; + int32_t right_edge[rwcfg->mem_dq_per_read_dqs]; + int32_t orig_mid_min, mid_min; + int32_t new_dqs, start_dqs, start_dqs_en = 0, final_dqs_en; + int32_t 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 (iocfg->shift_dqs_en_when_shift_dqs) + start_dqs_en = readl(addr - iocfg->dqs_en_delay_offset); + + /* set the left and right edge of each bit to an illegal value */ + /* use (iocfg->io_in_delay_max + 1) as an illegal value */ + sticky_bit_chk = 0; + for (i = 0; i < rwcfg->mem_dq_per_read_dqs; i++) { + left_edge[i] = iocfg->io_in_delay_max + 1; + right_edge[i] = iocfg->io_in_delay_max + 1; + } + + /* Search for the left edge of the window for each bit */ + search_left_edge(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(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 (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(rw_group * + rwcfg->mem_dq_per_read_dqs + i, + CAL_STAGE_VFIFO, + CAL_SUBSTAGE_VFIFO_CENTER); + } else { + set_failing_group_stage(rw_group * + rwcfg->mem_dq_per_read_dqs + i, + CAL_STAGE_VFIFO_AFTER_WRITES, + CAL_SUBSTAGE_VFIFO_CENTER); + } + return -EIO; + } + + min_index = get_window_mid_index(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 > iocfg->dqs_in_delay_max) + new_dqs = 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 (iocfg->shift_dqs_en_when_shift_dqs) { + if (start_dqs_en - mid_min > iocfg->dqs_en_delay_max) + mid_min += start_dqs_en - mid_min - + 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, + 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(0, left_edge, right_edge, mid_min, orig_mid_min, + min_index, test_bgn, &dq_margin, &dqs_margin); + + /* Move DQS-en */ + if (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(const u32 rw_group, + const u32 phase) +{ + int ret; + + /* Set a particular DQ/DQS phase. */ + scc_mgr_set_dqdqs_output_phase_all_ranks(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(0, 1); + + if (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(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(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 = iocfg->io_in_delay_max / + (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 < rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + for (i = 0, p = test_bgn, d = 0; + i < 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(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 < rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + scc_mgr_apply_group_dq_in_delay(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(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 < rwcfg->mem_number_of_ranks; + rank_bgn += NUM_RANKS_PER_SHADOW_REG, sr++) { + ret = rw_mgr_mem_calibrate_vfifo_center(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(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(iocfg->delay_per_opa_tap, + 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( + rw_group, d); + } + + for (p = 0; p <= iocfg->dqdqs_out_phase_max; p++) { + /* 1) Guaranteed Write */ + ret = rw_mgr_mem_calibrate_guaranteed_write(rw_group, p); + if (ret) + break; + + /* 2) DQS Enable Calibration */ + ret = rw_mgr_mem_calibrate_dqs_enable_calibration(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(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(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(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(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(rw_group, test_bgn, 0, 1); + if (ret) + set_failing_group_stage(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(void) +{ + 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(0, 1); + + do { + writel(gbl->curr_read_lat, &phy_mgr_cfg->phy_rlat); + debug_cond(DLEVEL == 2, "%s:%d lfifo: read_lat=%u", + __func__, __LINE__, gbl->curr_read_lat); + + if (!rw_mgr_mem_calibrate_read_test_all_ranks(0, NUM_READ_TESTS, + PASS_ALL_BITS, 1)) + break; + + found_one = 1; + /* + * Reduce read latency and see if things are + * working correctly. + */ + gbl->curr_read_lat--; + } while (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 */ + gbl->curr_read_lat += 2; + writel(gbl->curr_read_lat, &phy_mgr_cfg->phy_rlat); + debug_cond(DLEVEL == 2, + "%s:%d lfifo: success: using read_lat=%u\n", + __func__, __LINE__, gbl->curr_read_lat); + } else { + set_failing_group_stage(0xff, CAL_STAGE_LFIFO, + CAL_SUBSTAGE_READ_LATENCY); + + debug_cond(DLEVEL == 2, + "%s:%d lfifo: failed at initial read_lat=%u\n", + __func__, __LINE__, 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(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 = 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(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(write_group, + d + new_dqs); + } + + writel(0, &sdr_scc_mgr->update); + + if (rw_mgr_mem_calibrate_write_test(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 == 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 = iocfg->io_out1_delay_max + 1; + *end_curr = 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 > 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(const u32 rank_bgn, const u32 write_group, + const u32 test_bgn) +{ + int i; + u32 sticky_bit_chk; + u32 min_index; + int left_edge[rwcfg->mem_dq_per_write_dqs]; + int right_edge[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 = iocfg->io_out1_delay_max + 1; + int end_curr = iocfg->io_out1_delay_max + 1; + int bgn_best = iocfg->io_out1_delay_max + 1; + int end_best = 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) + + (rwcfg->mem_dq_per_write_dqs << 2)); + + /* Per-bit deskew. */ + + /* + * Set the left and right edge of each bit to an illegal value. + * Use (iocfg->io_out1_delay_max + 1) as an illegal value. + */ + sticky_bit_chk = 0; + for (i = 0; i < rwcfg->mem_dq_per_write_dqs; i++) { + left_edge[i] = iocfg->io_out1_delay_max + 1; + right_edge[i] = iocfg->io_out1_delay_max + 1; + } + + /* Search for the left edge of the window for each bit. */ + search_left_edge(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(1, rank_bgn, write_group, 0, + start_dqs, 0, + &sticky_bit_chk, + left_edge, right_edge, 0); + if (ret) { + set_failing_group_stage(test_bgn + ret - 1, CAL_STAGE_WRITES, + CAL_SUBSTAGE_WRITES_CENTER); + return -EINVAL; + } + + min_index = get_window_mid_index(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(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(write_group, new_dqs); + writel(0, &sdr_scc_mgr->update); + + /* Centre DM */ + debug_cond(DLEVEL == 2, "%s:%d write_center: DM\n", __func__, __LINE__); + + /* + * Set the left and right edge of each bit to an illegal value. + * Use (iocfg->io_out1_delay_max + 1) as an illegal value. + */ + left_edge[0] = iocfg->io_out1_delay_max + 1; + right_edge[0] = iocfg->io_out1_delay_max + 1; + + /* Search for the/part of the window with DM shift. */ + search_window(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(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 = iocfg->io_out1_delay_max + 1; + end_curr = iocfg->io_out1_delay_max + 1; + } + + /* Search for the/part of the window with DQS shifts. */ + search_window(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(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(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. */ + 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(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(rank_bgn, group, test_bgn); + if (ret) + set_failing_group_stage(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(void) +{ + int r; + + for (r = 0; r < rwcfg->mem_number_of_ranks; r++) { + /* Set rank. */ + set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_OFF); + + /* Precharge all banks. */ + writel(rwcfg->precharge_all, SDR_PHYGRP_RWMGRGRP_ADDRESS | + RW_MGR_RUN_SINGLE_GROUP_OFFSET); + + writel(0x0F, &sdr_rw_load_mgr_regs->load_cntr0); + writel(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(rwcfg->activate_0_and_1_wait2, + &sdr_rw_load_jump_mgr_regs->load_jump_add1); + + /* Activate rows. */ + writel(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(void) +{ + /* + * 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 << 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); + + 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. */ + gbl->curr_read_lat = rlat + 16; + if (gbl->curr_read_lat > max_latency) + gbl->curr_read_lat = max_latency; + + writel(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(void) +{ + 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 < rwcfg->mem_number_of_ranks; + r += NUM_RANKS_PER_SHADOW_REG) { + /* + * Set output phase alignment settings appropriate for + * skip calibration. + */ + for (i = 0; i < rwcfg->mem_if_read_dqs_width; i++) { + scc_mgr_set_dqs_en_phase(i, 0); + if (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 / iocfg->dll_chain_length) + * + * Dividing the above by (360 / iocfg->dll_chain_length) + * gives us the number of ptaps, which simplies to: + * + * (1.25 * iocfg->dll_chain_length - 2) + */ + scc_mgr_set_dqdqs_output_phase(i, + 1.25 * iocfg->dll_chain_length - 2); + } + writel(0xff, &sdr_scc_mgr->dqs_ena); + writel(0xff, &sdr_scc_mgr->dqs_io_ena); + + for (i = 0; i < 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 < 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 = 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. + */ + gbl->curr_read_lat = misccfg->calib_lfifo_offset; + writel(gbl->curr_read_lat, &phy_mgr_cfg->phy_rlat); +} + +/** + * mem_calibrate() - Memory calibration entry point. + * + * Perform memory calibration. + */ +static u32 mem_calibrate(void) +{ + 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 = rwcfg->mem_if_read_dqs_width / + rwcfg->mem_if_write_dqs_width; + + debug("%s:%d\n", __func__, __LINE__); + + /* Initialize the data settings */ + gbl->error_substage = CAL_SUBSTAGE_NIL; + gbl->error_stage = CAL_STAGE_NIL; + gbl->error_group = 0xff; + gbl->fom_in = 0; + gbl->fom_out = 0; + + /* Initialize WLAT and RLAT. */ + mem_init_latency(); + + /* Initialize bit slips. */ + mem_precharge_and_activate(); + + for (i = 0; i < 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 ((dyn_calib_steps & CALIB_SKIP_ALL) == CALIB_SKIP_ALL) { + /* + * Set VFIFO and LFIFO to instant-on settings in skip + * calibration mode. + */ + mem_skip_calibrate(); + + /* + * 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(); + + run_groups = ~0; + + for (write_group = 0, write_test_bgn = 0; write_group + < rwcfg->mem_if_write_dqs_width; write_group++, + write_test_bgn += 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(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 += rwcfg->mem_dq_per_read_dqs) { + if (STATIC_CALIB_STEPS & CALIB_SKIP_VFIFO) + continue; + + /* Calibrate the VFIFO */ + if (rw_mgr_mem_calibrate_vfifo(read_group, + read_test_bgn)) + continue; + + if (!(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 < 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(rank_bgn, + write_group, + write_test_bgn)) + continue; + + group_failed = 1; + if (!(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 += rwcfg->mem_dq_per_read_dqs) { + if (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES) + continue; + + if (!rw_mgr_mem_calibrate_vfifo_end(read_group, + read_test_bgn)) + continue; + + if (!(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()) + 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(void) +{ + int pass; + + 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. */ + clrbits_le32(&sdr_ctrl->ctrl_cfg, 1 << 22); + + phy_mgr_initialize(); + rw_mgr_mem_initialize(); + + /* Perform the actual memory calibration. */ + pass = mem_calibrate(); + + mem_precharge_and_activate(); + writel(0, &phy_mgr_cmd->fifo_reset); + + /* Handoff. */ + rw_mgr_mem_handoff(); + /* + * 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. */ + setbits_le32(&sdr_ctrl->ctrl_cfg, 1 << 22); + + 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(int pass) +{ + u32 debug_info; + + if (pass) { + printf("%s: CALIBRATION PASSED\n", __FILE__); + + gbl->fom_in /= 2; + gbl->fom_out /= 2; + + if (gbl->fom_in > 0xff) + gbl->fom_in = 0xff; + + if (gbl->fom_out > 0xff) + gbl->fom_out = 0xff; + + /* Update the FOM in the register file */ + debug_info = gbl->fom_in; + debug_info |= 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 { + printf("%s: CALIBRATION FAILED\n", __FILE__); + + debug_info = gbl->error_stage; + debug_info |= gbl->error_substage << 8; + debug_info |= 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 = gbl->error_stage; + debug_info |= gbl->error_substage << 8; + debug_info |= gbl->error_group << 16; + writel(debug_info, &sdr_reg_file->failing_stage); + } + + printf("%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(void) +{ + /* Initialize the register file with the correct data */ + writel(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(void) +{ + /* + * Initialize the register file with the correct data. + * Compute usable version of value in case we skip full + * computation later. + */ + writel(DIV_ROUND_UP(iocfg->delay_per_opa_tap, + 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 */ + writel((rwcfg->idle << 24) | (rwcfg->activate_1 << 16) | + (rwcfg->sgle_read << 8) | (rwcfg->precharge_all << 0), + &sdr_reg_file->trk_rw_mgr_addr); + + writel(rwcfg->mem_if_read_dqs_width, + &sdr_reg_file->trk_read_dqs_width); + + /* trefi [7:0] */ + writel((rwcfg->refresh_all << 24) | (1000 << 0), + &sdr_reg_file->trk_rfsh); +} + +int sdram_calibration_full(void) +{ + struct param_type my_param; + struct gbl_type my_gbl; + u32 pass; + + memset(&my_param, 0, sizeof(my_param)); + memset(&my_gbl, 0, sizeof(my_gbl)); + + param = &my_param; + gbl = &my_gbl; + + rwcfg = socfpga_get_sdram_rwmgr_config(); + iocfg = socfpga_get_sdram_io_config(); + misccfg = socfpga_get_sdram_misc_config(); + + /* Set the calibration enabled by default */ + 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 + gbl->phy_debug_mode_flags |= PHY_DEBUG_DISABLE_GUARANTEED_READ; +#endif + /* Initialize the register file */ + initialize_reg_file(); + + /* Initialize any PHY CSR */ + initialize_hps_phy(); + + scc_mgr_initialize(); + + initialize_tracking(); + + printf("%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 ", + rwcfg->mem_number_of_ranks, rwcfg->mem_number_of_cs_per_dimm, + rwcfg->mem_dq_per_read_dqs, rwcfg->mem_dq_per_write_dqs, + rwcfg->mem_virtual_groups_per_read_dqs, + rwcfg->mem_virtual_groups_per_write_dqs); + debug_cond(DLEVEL == 1, + "dqs=%u,%u dq=%u dm=%u ptap_delay=%u dtap_delay=%u ", + rwcfg->mem_if_read_dqs_width, rwcfg->mem_if_write_dqs_width, + rwcfg->mem_data_width, rwcfg->mem_data_mask_width, + iocfg->delay_per_opa_tap, iocfg->delay_per_dchain_tap); + debug_cond(DLEVEL == 1, "dtap_dqsen_delay=%u, dll=%u", + iocfg->delay_per_dqs_en_dchain_tap, iocfg->dll_chain_length); + debug_cond(DLEVEL == 1, + "max values: en_p=%u dqdqs_p=%u en_d=%u dqs_in_d=%u ", + iocfg->dqs_en_phase_max, iocfg->dqdqs_out_phase_max, + iocfg->dqs_en_delay_max, iocfg->dqs_in_delay_max); + debug_cond(DLEVEL == 1, "io_in_d=%u io_out1_d=%u io_out2_d=%u ", + iocfg->io_in_delay_max, iocfg->io_out1_delay_max, + iocfg->io_out2_delay_max); + debug_cond(DLEVEL == 1, "dqs_in_reserve=%u dqs_out_reserve=%u\n", + iocfg->dqs_in_reserve, 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. + */ + dyn_calib_steps = STATIC_CALIB_STEPS; + /* + * Load global to allow dynamic selection of delay loop settings + * based on calibration mode. + */ + if (!(dyn_calib_steps & CALIB_SKIP_DELAY_LOOPS)) + skip_delay_mask = 0xff; + else + skip_delay_mask = 0x0; + + pass = run_mem_calibrate(); + debug_mem_calibrate(pass); + return pass; +} diff --git a/drivers/ddr/altera/sequencer.h b/drivers/ddr/altera/sequencer.h new file mode 100644 index 00000000000..839a374968a --- /dev/null +++ b/drivers/ddr/altera/sequencer.h @@ -0,0 +1,227 @@ +/* + * Copyright Altera Corporation (C) 2012-2015 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _SEQUENCER_H_ +#define _SEQUENCER_H_ + +#define RW_MGR_NUM_DM_PER_WRITE_GROUP (rwcfg->mem_data_mask_width \ + / rwcfg->mem_if_write_dqs_width) +#define RW_MGR_NUM_TRUE_DM_PER_WRITE_GROUP (rwcfg->true_mem_data_mask_width \ + / rwcfg->mem_if_write_dqs_width) + +#define RW_MGR_NUM_DQS_PER_WRITE_GROUP (rwcfg->mem_if_read_dqs_width \ + / rwcfg->mem_if_write_dqs_width) +#define NUM_RANKS_PER_SHADOW_REG (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; +}; +#endif /* _SEQUENCER_H_ */ diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c index 63b3566e3ed..4448250f5c6 100644 --- a/drivers/fpga/socfpga.c +++ b/drivers/fpga/socfpga.c @@ -160,10 +160,13 @@ static void fpgamgr_program_write(const void *rbf_data, unsigned long rbf_size) " sub %1, #32\n" " subs %2, #1\n" " bne 1b\n" + " cmp %3, #0\n" + " beq 3f\n" "2: ldr %2, [%0], #4\n" " str %2, [%1]\n" " subs %3, #1\n" " bne 2b\n" + "3: nop\n" : "+r"(src), "+r"(dst), "+r"(loops32), "+r"(loops4) : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "cc"); } diff --git a/drivers/net/designware.c b/drivers/net/designware.c index bcae842389a..d9cb5076951 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -608,10 +608,11 @@ static int designware_eth_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id designware_eth_ids[] = { { .compatible = "allwinner,sun7i-a20-gmac" }, + { .compatible = "altr,socfpga-stmmac" }, { } }; -U_BOOT_DRIVER(eth_sandbox) = { +U_BOOT_DRIVER(eth_designware) = { .name = "eth_designware", .id = UCLASS_ETH, .of_match = designware_eth_ids, diff --git a/include/configs/socfpga_arria5.h b/include/configs/socfpga_arria5.h index 7aee1ce315e..e1cd9cc8aaf 100644 --- a/include/configs/socfpga_arria5.h +++ b/include/configs/socfpga_arria5.h @@ -7,9 +7,6 @@ #define __CONFIG_SOCFPGA_ARRIA5_H__ #include <asm/arch/socfpga_base_addrs.h> -#include "../../board/altera/socfpga/pinmux_config.h" -#include "../../board/altera/socfpga/iocsr_config.h" -#include "../../board/altera/socfpga/pll_config.h" /* U-Boot Commands */ #define CONFIG_SYS_NO_FLASH @@ -46,13 +43,11 @@ #else #define CONFIG_BOOTCOMMAND "run mmcload; run mmcboot" #endif -#define CONFIG_LOADADDR 0x8000 +#define CONFIG_LOADADDR 0x01000000 #define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR /* Ethernet on SoC (EMAC) */ #if defined(CONFIG_CMD_NET) -#define CONFIG_EMAC_BASE SOCFPGA_EMAC1_ADDRESS -#define CONFIG_PHY_INTERFACE_MODE PHY_INTERFACE_MODE_RGMII /* PHY */ #define CONFIG_PHY_MICREL diff --git a/include/configs/socfpga_common.h b/include/configs/socfpga_common.h index e8473b872ad..5ca45a90c79 100644 --- a/include/configs/socfpga_common.h +++ b/include/configs/socfpga_common.h @@ -18,10 +18,13 @@ */ #define CONFIG_DISPLAY_CPUINFO #define CONFIG_DISPLAY_BOARDINFO_LATE +#define CONFIG_ARCH_MISC_INIT #define CONFIG_ARCH_EARLY_INIT_R #define CONFIG_SYS_NO_FLASH #define CONFIG_CLOCKS +#define CONFIG_CRC32_VERIFY + #define CONFIG_FIT #define CONFIG_OF_LIBFDT #define CONFIG_SYS_BOOTMAPSZ (64 * 1024 * 1024) @@ -38,10 +41,11 @@ #define CONFIG_SYS_MEMTEST_END PHYS_SDRAM_1_SIZE #define CONFIG_SYS_INIT_RAM_ADDR 0xFFFF0000 -#define CONFIG_SYS_INIT_RAM_SIZE (0x10000 - CONFIG_SYS_SPL_MALLOC_SIZE) -#define CONFIG_SYS_INIT_SP_ADDR \ - (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_RAM_SIZE - \ - GENERATED_GBL_DATA_SIZE) +#define CONFIG_SYS_INIT_RAM_SIZE 0x10000 +#define CONFIG_SYS_INIT_SP_OFFSET \ + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) +#define CONFIG_SYS_INIT_SP_ADDR \ + (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 #ifdef CONFIG_SOCFPGA_VIRTUAL_TARGET @@ -75,6 +79,11 @@ #define CONFIG_SYS_PL310_BASE SOCFPGA_MPUL2_ADDRESS /* + * SDRAM controller + */ +#define CONFIG_ALTERA_SDRAM + +/* * EPCS/EPCQx1 Serial Flash Controller */ #ifdef CONFIG_ALTERA_SPI @@ -188,7 +197,13 @@ unsigned int cm_get_l4_sp_clk_hz(void); /* Enable multiple SPI NOR flash manufacturers */ #define CONFIG_SPI_FLASH_STMICRO /* Micron/Numonyx flash */ #define CONFIG_SPI_FLASH_SPANSION /* Spansion flash */ +#ifndef CONFIG_SPL_BUILD #define CONFIG_SPI_FLASH_MTD +#define CONFIG_CMD_MTDPARTS +#define CONFIG_MTD_DEVICE +#define CONFIG_MTD_PARTITIONS +#define MTDIDS_DEFAULT "nor0=ff705000.spi" +#endif /* QSPI reference clock */ #ifndef __ASSEMBLY__ unsigned int cm_get_qspi_controller_clk_hz(void); @@ -196,6 +211,7 @@ unsigned int cm_get_qspi_controller_clk_hz(void); #endif #define CONFIG_CQSPI_DECODER 0 #define CONFIG_CMD_SF +#define CONFIG_SPI_FLASH_BAR #endif #ifdef CONFIG_OF_CONTROL /* DW SPI is controlled via DT */ @@ -282,31 +298,44 @@ unsigned int cm_get_qspi_controller_clk_hz(void); * 0xFFFF_FF00 ...... End of SRAM */ #define CONFIG_SPL_FRAMEWORK -#define CONFIG_SPL_BOARD_INIT #define CONFIG_SPL_RAM_DEVICE #define CONFIG_SPL_TEXT_BASE CONFIG_SYS_INIT_RAM_ADDR -#define CONFIG_SYS_SPL_MALLOC_START CONFIG_SYS_INIT_SP_ADDR -#define CONFIG_SYS_SPL_MALLOC_SIZE (5 * 1024) #define CONFIG_SPL_MAX_SIZE (64 * 1024) - -#define CHUNKSZ_CRC32 (1 * 1024) /* FIXME: ewww */ -#define CONFIG_CRC32_VERIFY - -/* Linker script for SPL */ -#define CONFIG_SPL_LDSCRIPT "arch/arm/mach-socfpga/u-boot-spl.lds" +#ifdef CONFIG_SPL_BUILD +#define CONFIG_SYS_MALLOC_SIMPLE +#endif #define CONFIG_SPL_LIBCOMMON_SUPPORT #define CONFIG_SPL_LIBGENERIC_SUPPORT #define CONFIG_SPL_WATCHDOG_SUPPORT #define CONFIG_SPL_SERIAL_SUPPORT +#define CONFIG_SPL_MMC_SUPPORT +#define CONFIG_SPL_SPI_SUPPORT + +/* SPL SDMMC boot support */ +#ifdef CONFIG_SPL_MMC_SUPPORT +#if defined(CONFIG_SPL_FAT_SUPPORT) || defined(CONFIG_SPL_EXT_SUPPORT) +#define CONFIG_SYS_MMCSD_FS_BOOT_PARTITION 2 +#define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot-dtb.img" +#define CONFIG_SPL_LIBDISK_SUPPORT +#else +#define CONFIG_SYS_MMCSD_FS_BOOT_PARTITION 3 +#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR 0xa00 /* offset 2560 sect (1M+256k) */ +#define CONFIG_SYS_U_BOOT_MAX_SIZE_SECTORS 800 /* 400 KB */ +#endif +#endif + +/* SPL QSPI boot support */ +#ifdef CONFIG_SPL_SPI_SUPPORT +#define CONFIG_DM_SEQ_ALIAS 1 +#define CONFIG_SPL_SPI_FLASH_SUPPORT +#define CONFIG_SPL_SPI_LOAD +#define CONFIG_SYS_SPI_U_BOOT_OFFS 0x40000 +#endif /* * Stack setup */ #define CONFIG_SPL_STACK CONFIG_SYS_INIT_SP_ADDR -#ifdef CONFIG_SPL_BUILD -#undef CONFIG_PARTITIONS -#endif - #endif /* __CONFIG_SOCFPGA_CYCLONE5_COMMON_H__ */ diff --git a/include/configs/socfpga_cyclone5.h b/include/configs/socfpga_cyclone5.h index 33d04fdc4a9..9b317413e7c 100644 --- a/include/configs/socfpga_cyclone5.h +++ b/include/configs/socfpga_cyclone5.h @@ -7,9 +7,6 @@ #define __CONFIG_SOCFPGA_CYCLONE5_H__ #include <asm/arch/socfpga_base_addrs.h> -#include "../../board/altera/socfpga/pinmux_config.h" -#include "../../board/altera/socfpga/iocsr_config.h" -#include "../../board/altera/socfpga/pll_config.h" /* U-Boot Commands */ #define CONFIG_SYS_NO_FLASH @@ -46,13 +43,11 @@ #else #define CONFIG_BOOTCOMMAND "run mmcload; run mmcboot" #endif -#define CONFIG_LOADADDR 0x8000 +#define CONFIG_LOADADDR 0x01000000 #define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR /* Ethernet on SoC (EMAC) */ #if defined(CONFIG_CMD_NET) -#define CONFIG_EMAC_BASE SOCFPGA_EMAC1_ADDRESS -#define CONFIG_PHY_INTERFACE_MODE PHY_INTERFACE_MODE_RGMII /* PHY */ #define CONFIG_PHY_MICREL diff --git a/include/fdtdec.h b/include/fdtdec.h index c9a5c9a9f94..eac679e0e3c 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -181,6 +181,7 @@ enum fdt_compat_id { COMPAT_SOCIONEXT_XHCI, /* Socionext UniPhier xHCI */ COMPAT_INTEL_PCH, /* Intel PCH */ COMPAT_INTEL_IRQ_ROUTER, /* Intel Interrupt Router */ + COMPAT_ALTERA_SOCFPGA_DWMAC, /* SoCFPGA Ethernet controller */ COMPAT_COUNT, }; diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 01531091038..b2017872fae 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -75,6 +75,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"), COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"), COMPAT(COMPAT_INTEL_IRQ_ROUTER, "intel,irq-router"), + COMPAT(ALTERA_SOCFPGA_DWMAC, "altr,socfpga-stmmac"), }; const char *fdtdec_get_compatible(enum fdt_compat_id id) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index b1047b5d09f..239ee488166 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -62,6 +62,7 @@ libs-$(CONFIG_SPL_MMC_SUPPORT) += drivers/mmc/ libs-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += drivers/ddr/fsl/ libs-$(CONFIG_SYS_MVEBU_DDR_A38X) += drivers/ddr/marvell/a38x/ libs-$(CONFIG_SYS_MVEBU_DDR_AXP) += drivers/ddr/marvell/axp/ +libs-$(CONFIG_ALTERA_SDRAM) += drivers/ddr/altera/ libs-$(CONFIG_SPL_SERIAL_SUPPORT) += drivers/serial/ libs-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/ libs-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/ @@ -162,6 +163,10 @@ ifdef CONFIG_SAMSUNG ALL-y += $(obj)/$(BOARD)-spl.bin endif +ifdef CONFIG_ARCH_SOCFPGA +ALL-y += $(obj)/$(SPL_BIN)-dtb.sfp +endif + ifdef CONFIG_SUNXI ALL-y += $(obj)/sunxi-spl.bin endif @@ -230,6 +235,12 @@ ifneq ($(CONFIG_SPL_TEXT_BASE),) LDFLAGS_$(SPL_BIN) += -Ttext $(CONFIG_SPL_TEXT_BASE) endif +ifdef CONFIG_ARCH_SOCFPGA +MKIMAGEFLAGS_$(SPL_BIN)-dtb.sfp = -T socfpgaimage +$(obj)/$(SPL_BIN)-dtb.sfp: $(obj)/$(SPL_BIN)-dtb.bin FORCE + $(call if_changed,mkimage) +endif + ifdef CONFIG_SUNXI quiet_cmd_mksunxiboot = MKSUNXI $@ cmd_mksunxiboot = $(objtree)/tools/mksunxiboot $< $@ |