From 82dacf2c1cfca0138cb612e86114b01789194533 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 21 Mar 2024 18:12:41 +1100 Subject: [PATCH] ECPIX-5: Wire up SPI flash The flash chip on my board is an ISSI IS26LP256P chip. The ISSI chip requires slightly different setup for quad mode from the other brands, but works fine with the existing SPI flash interface logic here. Signed-off-by: Paul Mackerras --- constraints/ecpix-5.lpf | 14 +++++++ fpga/top-ecpix5.vhdl | 62 ++++++++++++++++++++++++++++-- litedram/gen-src/sdram_init/main.c | 48 ++++++++++++++++++++--- 3 files changed, 116 insertions(+), 8 deletions(-) diff --git a/constraints/ecpix-5.lpf b/constraints/ecpix-5.lpf index 6517b0f..1748601 100644 --- a/constraints/ecpix-5.lpf +++ b/constraints/ecpix-5.lpf @@ -43,3 +43,17 @@ LOCATE COMP "led8_b_n" SITE "P22"; IOBUF PORT "led8_r_n" IO_TYPE=LVCMOS33; IOBUF PORT "led8_g_n" IO_TYPE=LVCMOS33; IOBUF PORT "led8_b_n" IO_TYPE=LVCMOS33; + +// We use USRMCLK instead for clk +// LOCATE COMP "spi_flash_clk" SITE "U16"; +// IOBUF PORT "spi_flash_clk" IO_TYPE=LVCMOS33; +LOCATE COMP "spi_flash_cs_n" SITE "AA2"; +IOBUF PORT "spi_flash_cs_n" IO_TYPE=LVCMOS33; +LOCATE COMP "spi_flash_mosi" SITE "AE2"; +IOBUF PORT "spi_flash_mosi" IO_TYPE=LVCMOS33; +LOCATE COMP "spi_flash_miso" SITE "AD2"; +IOBUF PORT "spi_flash_miso" IO_TYPE=LVCMOS33; +LOCATE COMP "spi_flash_wp_n" SITE "AF2"; +IOBUF PORT "spi_flash_wp_n" IO_TYPE=LVCMOS33; +LOCATE COMP "spi_flash_hold_n" SITE "AE1"; +IOBUF PORT "spi_flash_hold_n" IO_TYPE=LVCMOS33; diff --git a/fpga/top-ecpix5.vhdl b/fpga/top-ecpix5.vhdl index 077b801..ee855dc 100644 --- a/fpga/top-ecpix5.vhdl +++ b/fpga/top-ecpix5.vhdl @@ -17,6 +17,9 @@ entity toplevel is USE_LITEDRAM : boolean := false; NO_BRAM : boolean := false; SCLK_STARTUPE2 : boolean := false; + SPI_FLASH_OFFSET : integer := 4194304; + SPI_FLASH_DEF_CKDV : natural := 0; + SPI_FLASH_DEF_QUAD : boolean := true; LOG_LENGTH : natural := 0; UART_IS_16550 : boolean := true; HAS_UART1 : boolean := false; @@ -45,7 +48,14 @@ entity toplevel is led7_b_n : out std_ulogic; led8_r_n : out std_ulogic; led8_g_n : out std_ulogic; - led8_b_n : out std_ulogic + led8_b_n : out std_ulogic; + + -- SPI + spi_flash_cs_n : out std_ulogic; + spi_flash_mosi : inout std_ulogic; + spi_flash_miso : inout std_ulogic; + spi_flash_wp_n : inout std_ulogic; + spi_flash_hold_n : inout std_ulogic ); end entity toplevel; @@ -60,6 +70,14 @@ architecture behaviour of toplevel is signal system_clk : std_ulogic; signal system_clk_locked : std_ulogic; + -- SPI flash + signal spi_sck : std_ulogic; + signal spi_sck_ts : std_ulogic; + signal spi_cs_n : std_ulogic; + signal spi_sdat_o : std_ulogic_vector(3 downto 0); + signal spi_sdat_oe : std_ulogic_vector(3 downto 0); + signal spi_sdat_i : std_ulogic_vector(3 downto 0); + -- Fixup various memory sizes based on generics function get_bram_size return natural is begin @@ -82,6 +100,15 @@ architecture behaviour of toplevel is constant BRAM_SIZE : natural := get_bram_size; constant PAYLOAD_SIZE : natural := get_payload_size; + COMPONENT USRMCLK + PORT( + USRMCLKI : IN STD_ULOGIC; + USRMCLKTS : IN STD_ULOGIC + ); + END COMPONENT; + attribute syn_noprune: boolean ; + attribute syn_noprune of USRMCLK: component is true; + begin -- Main SoC @@ -96,7 +123,11 @@ begin HAS_DRAM => USE_LITEDRAM, DRAM_SIZE => 512 * 1024 * 1024, DRAM_INIT_SIZE => PAYLOAD_SIZE, - HAS_SPI_FLASH => false, + HAS_SPI_FLASH => true, + SPI_FLASH_DLINES => 4, + SPI_FLASH_OFFSET => SPI_FLASH_OFFSET, + SPI_FLASH_DEF_CKDV => SPI_FLASH_DEF_CKDV, + SPI_FLASH_DEF_QUAD => SPI_FLASH_DEF_QUAD, LOG_LENGTH => LOG_LENGTH, UART0_IS_16550 => UART_IS_16550, HAS_UART1 => HAS_UART1, @@ -111,9 +142,34 @@ begin -- UART signals uart0_txd => uart0_txd, - uart0_rxd => uart0_rxd + uart0_rxd => uart0_rxd, + + -- SPI signals + spi_flash_sck => spi_sck, + spi_flash_cs_n => spi_cs_n, + spi_flash_sdat_o => spi_sdat_o, + spi_flash_sdat_oe => spi_sdat_oe, + spi_flash_sdat_i => spi_sdat_i ); + -- SPI Flash + -- + spi_flash_cs_n <= spi_cs_n; + spi_flash_mosi <= spi_sdat_o(0) when spi_sdat_oe(0) = '1' else 'Z'; + spi_flash_miso <= spi_sdat_o(1) when spi_sdat_oe(1) = '1' else 'Z'; + spi_flash_wp_n <= spi_sdat_o(2) when spi_sdat_oe(2) = '1' else 'Z'; + spi_flash_hold_n <= spi_sdat_o(3) when spi_sdat_oe(3) = '1' else 'Z'; + spi_sdat_i(0) <= spi_flash_mosi; + spi_sdat_i(1) <= spi_flash_miso; + spi_sdat_i(2) <= spi_flash_wp_n; + spi_sdat_i(3) <= spi_flash_hold_n; + spi_sck_ts <= '0'; + + uclk: USRMCLK port map ( + USRMCLKI => spi_sck, + USRMCLKTS => spi_sck_ts + ); + nodram: if not USE_LITEDRAM generate signal div2 : std_ulogic := '0'; begin diff --git a/litedram/gen-src/sdram_init/main.c b/litedram/gen-src/sdram_init/main.c index 2d99410..9bf98d4 100644 --- a/litedram/gen-src/sdram_init/main.c +++ b/litedram/gen-src/sdram_init/main.c @@ -41,6 +41,7 @@ void flush_cpu_icache(void) #define SPI_CMD_READ 0x03 #define SPI_CMD_DUAL_FREAD 0x3b #define SPI_CMD_QUAD_FREAD 0x6b +#define SPI_CMD_QUAD_FREAD_4BA 0x6c #define SPI_CMD_RDCR 0x35 #define SPI_CMD_WREN 0x06 #define SPI_CMD_PP 0x02 @@ -106,10 +107,44 @@ static void check_spansion_quad_mode(void) wait_wip(); } +static uint32_t check_enable_issi_quad(void) +{ + uint8_t sr; + + /* Read status register to see if quad mode is already enabled */ + fl_cs_on(); + writeb(SPI_CMD_RDSR, SPI_FCTRL_BASE + SPI_REG_DATA); + sr = readb(SPI_FCTRL_BASE + SPI_REG_DATA); + fl_cs_off(); + if ((sr & 0x40) == 0) { + printf(" [enabling quad]"); + send_wren(); + fl_cs_on(); + writeb(SPI_CMD_WWR, SPI_FCTRL_BASE + SPI_REG_DATA); + writeb(sr | 0x40, SPI_FCTRL_BASE + SPI_REG_DATA); + fl_cs_off(); + wait_wip(); + } + + /* Enable quad mode and 4B addresses, 8 dummy cycles */ + return SPI_CMD_QUAD_FREAD_4BA | + (0x07 << SPI_REG_AUTO_CFG_DUMMIES_SHIFT) | + SPI_REG_AUT_CFG_MODE_QUAD | SPI_REG_AUTO_CFG_ADDR4 | + (0x20 << SPI_REG_AUTO_CFG_CSTOUT_SHIFT); +} + static bool check_flash(void) { bool quad = false; uint8_t id[3]; + uint32_t autocfg; + + /* default auto mode configuration for quad reads: */ + /* Enable quad mode, 8 dummy clocks, 32 cycles CS timeout */ + autocfg = SPI_CMD_QUAD_FREAD | + (0x07 << SPI_REG_AUTO_CFG_DUMMIES_SHIFT) | + SPI_REG_AUT_CFG_MODE_QUAD | + (0x20 << SPI_REG_AUTO_CFG_CSTOUT_SHIFT); fl_cs_on(); writeb(SPI_CMD_RDID, SPI_FCTRL_BASE + SPI_REG_DATA); @@ -134,6 +169,13 @@ static bool check_flash(void) printf(" Micron"); quad = true; } + if (id[0] == 0x9d && (id[1] & ~0x10) == 0x60 && + id[2] == 0x19) { + /* ISSI IS25LP256D or IS25WP256D */ + printf(" ISSI"); + autocfg = check_enable_issi_quad(); + quad = true; + } if (quad) { uint32_t cfg; printf(" [quad IO mode]"); @@ -141,12 +183,8 @@ static bool check_flash(void) /* Preserve the default clock div for the board */ cfg = readl(SPI_FCTRL_BASE + SPI_REG_AUTO_CFG); cfg &= SPI_REG_AUTO_CFG_CKDIV_MASK; + cfg |= autocfg; - /* Enable quad mode, 8 dummy clocks, 32 cycles CS timeout */ - cfg |= SPI_CMD_QUAD_FREAD | - (0x07 << SPI_REG_AUTO_CFG_DUMMIES_SHIFT) | - SPI_REG_AUT_CFG_MODE_QUAD | - (0x20 << SPI_REG_AUTO_CFG_CSTOUT_SHIFT); writel(cfg, SPI_FCTRL_BASE + SPI_REG_AUTO_CFG); } printf("\n");