16b57ff6fSAlison Wang /* 25bc48308SHaikun.Wang@freescale.com * Copyright 2013-2015 Freescale Semiconductor, Inc. 36b57ff6fSAlison Wang * 46b57ff6fSAlison Wang * Freescale Quad Serial Peripheral Interface (QSPI) driver 56b57ff6fSAlison Wang * 66b57ff6fSAlison Wang * SPDX-License-Identifier: GPL-2.0+ 76b57ff6fSAlison Wang */ 86b57ff6fSAlison Wang 96b57ff6fSAlison Wang #include <common.h> 106b57ff6fSAlison Wang #include <malloc.h> 116b57ff6fSAlison Wang #include <spi.h> 126b57ff6fSAlison Wang #include <asm/io.h> 136b57ff6fSAlison Wang #include <linux/sizes.h> 145bc48308SHaikun.Wang@freescale.com #include <dm.h> 155bc48308SHaikun.Wang@freescale.com #include <errno.h> 16beedbc2eSAlexander Stein #include <watchdog.h> 176b57ff6fSAlison Wang #include "fsl_qspi.h" 186b57ff6fSAlison Wang 195bc48308SHaikun.Wang@freescale.com DECLARE_GLOBAL_DATA_PTR; 205bc48308SHaikun.Wang@freescale.com 216b57ff6fSAlison Wang #define RX_BUFFER_SIZE 0x80 22b93ab2eeSPeng Fan #ifdef CONFIG_MX6SX 23b93ab2eeSPeng Fan #define TX_BUFFER_SIZE 0x200 24b93ab2eeSPeng Fan #else 256b57ff6fSAlison Wang #define TX_BUFFER_SIZE 0x40 26b93ab2eeSPeng Fan #endif 276b57ff6fSAlison Wang 288770413fSGong Qianyu #define OFFSET_BITS_MASK GENMASK(23, 0) 296b57ff6fSAlison Wang 306b57ff6fSAlison Wang #define FLASH_STATUS_WEL 0x02 316b57ff6fSAlison Wang 326b57ff6fSAlison Wang /* SEQID */ 336b57ff6fSAlison Wang #define SEQID_WREN 1 346b57ff6fSAlison Wang #define SEQID_FAST_READ 2 356b57ff6fSAlison Wang #define SEQID_RDSR 3 366b57ff6fSAlison Wang #define SEQID_SE 4 376b57ff6fSAlison Wang #define SEQID_CHIP_ERASE 5 386b57ff6fSAlison Wang #define SEQID_PP 6 396b57ff6fSAlison Wang #define SEQID_RDID 7 40ba4dc8abSPeng Fan #define SEQID_BE_4K 8 41a2358783SPeng Fan #ifdef CONFIG_SPI_FLASH_BAR 42a2358783SPeng Fan #define SEQID_BRRD 9 43a2358783SPeng Fan #define SEQID_BRWR 10 44a2358783SPeng Fan #define SEQID_RDEAR 11 45a2358783SPeng Fan #define SEQID_WREAR 12 46a2358783SPeng Fan #endif 476b57ff6fSAlison Wang 4853e3db7fSPeng Fan /* QSPI CMD */ 4953e3db7fSPeng Fan #define QSPI_CMD_PP 0x02 /* Page program (up to 256 bytes) */ 5053e3db7fSPeng Fan #define QSPI_CMD_RDSR 0x05 /* Read status register */ 5153e3db7fSPeng Fan #define QSPI_CMD_WREN 0x06 /* Write enable */ 5253e3db7fSPeng Fan #define QSPI_CMD_FAST_READ 0x0b /* Read data bytes (high frequency) */ 53ba4dc8abSPeng Fan #define QSPI_CMD_BE_4K 0x20 /* 4K erase */ 5453e3db7fSPeng Fan #define QSPI_CMD_CHIP_ERASE 0xc7 /* Erase whole flash chip */ 5553e3db7fSPeng Fan #define QSPI_CMD_SE 0xd8 /* Sector erase (usually 64KiB) */ 5653e3db7fSPeng Fan #define QSPI_CMD_RDID 0x9f /* Read JEDEC ID */ 576b57ff6fSAlison Wang 58a2358783SPeng Fan /* Used for Micron, winbond and Macronix flashes */ 59a2358783SPeng Fan #define QSPI_CMD_WREAR 0xc5 /* EAR register write */ 60a2358783SPeng Fan #define QSPI_CMD_RDEAR 0xc8 /* EAR reigster read */ 61a2358783SPeng Fan 62a2358783SPeng Fan /* Used for Spansion flashes only. */ 63a2358783SPeng Fan #define QSPI_CMD_BRRD 0x16 /* Bank register read */ 64a2358783SPeng Fan #define QSPI_CMD_BRWR 0x17 /* Bank register write */ 65a2358783SPeng Fan 6653e3db7fSPeng Fan /* 4-byte address QSPI CMD - used on Spansion and some Macronix flashes */ 6753e3db7fSPeng Fan #define QSPI_CMD_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ 6853e3db7fSPeng Fan #define QSPI_CMD_PP_4B 0x12 /* Page program (up to 256 bytes) */ 6953e3db7fSPeng Fan #define QSPI_CMD_SE_4B 0xdc /* Sector erase (usually 64KiB) */ 706b57ff6fSAlison Wang 715bc48308SHaikun.Wang@freescale.com /* fsl_qspi_platdata flags */ 7229e6abd9SJagan Teki #define QSPI_FLAG_REGMAP_ENDIAN_BIG BIT(0) 735bc48308SHaikun.Wang@freescale.com 745bc48308SHaikun.Wang@freescale.com /* default SCK frequency, unit: HZ */ 755bc48308SHaikun.Wang@freescale.com #define FSL_QSPI_DEFAULT_SCK_FREQ 50000000 765bc48308SHaikun.Wang@freescale.com 775bc48308SHaikun.Wang@freescale.com /* QSPI max chipselect signals number */ 785bc48308SHaikun.Wang@freescale.com #define FSL_QSPI_MAX_CHIPSELECT_NUM 4 795bc48308SHaikun.Wang@freescale.com 805bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_DM_SPI 815bc48308SHaikun.Wang@freescale.com /** 825bc48308SHaikun.Wang@freescale.com * struct fsl_qspi_platdata - platform data for Freescale QSPI 835bc48308SHaikun.Wang@freescale.com * 845bc48308SHaikun.Wang@freescale.com * @flags: Flags for QSPI QSPI_FLAG_... 855bc48308SHaikun.Wang@freescale.com * @speed_hz: Default SCK frequency 865bc48308SHaikun.Wang@freescale.com * @reg_base: Base address of QSPI registers 875bc48308SHaikun.Wang@freescale.com * @amba_base: Base address of QSPI memory mapping 885bc48308SHaikun.Wang@freescale.com * @amba_total_size: size of QSPI memory mapping 895bc48308SHaikun.Wang@freescale.com * @flash_num: Number of active slave devices 905bc48308SHaikun.Wang@freescale.com * @num_chipselect: Number of QSPI chipselect signals 915bc48308SHaikun.Wang@freescale.com */ 925bc48308SHaikun.Wang@freescale.com struct fsl_qspi_platdata { 935bc48308SHaikun.Wang@freescale.com u32 flags; 945bc48308SHaikun.Wang@freescale.com u32 speed_hz; 955bc48308SHaikun.Wang@freescale.com u32 reg_base; 965bc48308SHaikun.Wang@freescale.com u32 amba_base; 975bc48308SHaikun.Wang@freescale.com u32 amba_total_size; 985bc48308SHaikun.Wang@freescale.com u32 flash_num; 995bc48308SHaikun.Wang@freescale.com u32 num_chipselect; 1005bc48308SHaikun.Wang@freescale.com }; 1016b57ff6fSAlison Wang #endif 1026b57ff6fSAlison Wang 1035bc48308SHaikun.Wang@freescale.com /** 1045bc48308SHaikun.Wang@freescale.com * struct fsl_qspi_priv - private data for Freescale QSPI 1055bc48308SHaikun.Wang@freescale.com * 1065bc48308SHaikun.Wang@freescale.com * @flags: Flags for QSPI QSPI_FLAG_... 1075bc48308SHaikun.Wang@freescale.com * @bus_clk: QSPI input clk frequency 1085bc48308SHaikun.Wang@freescale.com * @speed_hz: Default SCK frequency 1095bc48308SHaikun.Wang@freescale.com * @cur_seqid: current LUT table sequence id 1105bc48308SHaikun.Wang@freescale.com * @sf_addr: flash access offset 1115bc48308SHaikun.Wang@freescale.com * @amba_base: Base address of QSPI memory mapping of every CS 1125bc48308SHaikun.Wang@freescale.com * @amba_total_size: size of QSPI memory mapping 1135bc48308SHaikun.Wang@freescale.com * @cur_amba_base: Base address of QSPI memory mapping of current CS 1145bc48308SHaikun.Wang@freescale.com * @flash_num: Number of active slave devices 1155bc48308SHaikun.Wang@freescale.com * @num_chipselect: Number of QSPI chipselect signals 1165bc48308SHaikun.Wang@freescale.com * @regs: Point to QSPI register structure for I/O access 1175bc48308SHaikun.Wang@freescale.com */ 1185bc48308SHaikun.Wang@freescale.com struct fsl_qspi_priv { 1195bc48308SHaikun.Wang@freescale.com u32 flags; 1205bc48308SHaikun.Wang@freescale.com u32 bus_clk; 1215bc48308SHaikun.Wang@freescale.com u32 speed_hz; 1225bc48308SHaikun.Wang@freescale.com u32 cur_seqid; 1235bc48308SHaikun.Wang@freescale.com u32 sf_addr; 1245bc48308SHaikun.Wang@freescale.com u32 amba_base[FSL_QSPI_MAX_CHIPSELECT_NUM]; 1255bc48308SHaikun.Wang@freescale.com u32 amba_total_size; 1265bc48308SHaikun.Wang@freescale.com u32 cur_amba_base; 1275bc48308SHaikun.Wang@freescale.com u32 flash_num; 1285bc48308SHaikun.Wang@freescale.com u32 num_chipselect; 1295bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs; 1306b57ff6fSAlison Wang }; 1316b57ff6fSAlison Wang 1325bc48308SHaikun.Wang@freescale.com #ifndef CONFIG_DM_SPI 1336b57ff6fSAlison Wang struct fsl_qspi { 1346b57ff6fSAlison Wang struct spi_slave slave; 1355bc48308SHaikun.Wang@freescale.com struct fsl_qspi_priv priv; 1366b57ff6fSAlison Wang }; 1375bc48308SHaikun.Wang@freescale.com #endif 1385bc48308SHaikun.Wang@freescale.com 1395bc48308SHaikun.Wang@freescale.com static u32 qspi_read32(u32 flags, u32 *addr) 1405bc48308SHaikun.Wang@freescale.com { 1415bc48308SHaikun.Wang@freescale.com return flags & QSPI_FLAG_REGMAP_ENDIAN_BIG ? 1425bc48308SHaikun.Wang@freescale.com in_be32(addr) : in_le32(addr); 1435bc48308SHaikun.Wang@freescale.com } 1445bc48308SHaikun.Wang@freescale.com 1455bc48308SHaikun.Wang@freescale.com static void qspi_write32(u32 flags, u32 *addr, u32 val) 1465bc48308SHaikun.Wang@freescale.com { 1475bc48308SHaikun.Wang@freescale.com flags & QSPI_FLAG_REGMAP_ENDIAN_BIG ? 1485bc48308SHaikun.Wang@freescale.com out_be32(addr, val) : out_le32(addr, val); 1495bc48308SHaikun.Wang@freescale.com } 1506b57ff6fSAlison Wang 1516b57ff6fSAlison Wang /* QSPI support swapping the flash read/write data 1526b57ff6fSAlison Wang * in hardware for LS102xA, but not for VF610 */ 1536b57ff6fSAlison Wang static inline u32 qspi_endian_xchg(u32 data) 1546b57ff6fSAlison Wang { 1556b57ff6fSAlison Wang #ifdef CONFIG_VF610 1566b57ff6fSAlison Wang return swab32(data); 1576b57ff6fSAlison Wang #else 1586b57ff6fSAlison Wang return data; 1596b57ff6fSAlison Wang #endif 1606b57ff6fSAlison Wang } 1616b57ff6fSAlison Wang 1625bc48308SHaikun.Wang@freescale.com static void qspi_set_lut(struct fsl_qspi_priv *priv) 1636b57ff6fSAlison Wang { 1645bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 1656b57ff6fSAlison Wang u32 lut_base; 1666b57ff6fSAlison Wang 1676b57ff6fSAlison Wang /* Unlock the LUT */ 1685bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lutkey, LUT_KEY_VALUE); 1695bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lckcr, QSPI_LCKCR_UNLOCK); 1706b57ff6fSAlison Wang 1716b57ff6fSAlison Wang /* Write Enable */ 1726b57ff6fSAlison Wang lut_base = SEQID_WREN * 4; 1735bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_WREN) | 1746b57ff6fSAlison Wang PAD0(LUT_PAD1) | INSTR0(LUT_CMD)); 1755bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 0); 1765bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 1775bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 1786b57ff6fSAlison Wang 1796b57ff6fSAlison Wang /* Fast Read */ 1806b57ff6fSAlison Wang lut_base = SEQID_FAST_READ * 4; 181a2358783SPeng Fan #ifdef CONFIG_SPI_FLASH_BAR 1825bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 1835bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_FAST_READ) | PAD0(LUT_PAD1) | 1845bc48308SHaikun.Wang@freescale.com INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 185a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 186a2358783SPeng Fan #else 1876b57ff6fSAlison Wang if (FSL_QSPI_FLASH_SIZE <= SZ_16M) 1885bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 1895bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_FAST_READ) | PAD0(LUT_PAD1) | 1905bc48308SHaikun.Wang@freescale.com INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 1916b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 1926b57ff6fSAlison Wang else 1935bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 19453e3db7fSPeng Fan OPRND0(QSPI_CMD_FAST_READ_4B) | 19553e3db7fSPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | 19653e3db7fSPeng Fan OPRND1(ADDR32BIT) | PAD1(LUT_PAD1) | 19753e3db7fSPeng Fan INSTR1(LUT_ADDR)); 198a2358783SPeng Fan #endif 1995bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 2005bc48308SHaikun.Wang@freescale.com OPRND0(8) | PAD0(LUT_PAD1) | INSTR0(LUT_DUMMY) | 2015bc48308SHaikun.Wang@freescale.com OPRND1(RX_BUFFER_SIZE) | PAD1(LUT_PAD1) | 2026b57ff6fSAlison Wang INSTR1(LUT_READ)); 2035bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 2045bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 2056b57ff6fSAlison Wang 2066b57ff6fSAlison Wang /* Read Status */ 2076b57ff6fSAlison Wang lut_base = SEQID_RDSR * 4; 2085bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_RDSR) | 2096b57ff6fSAlison Wang PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | 2106b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_READ)); 2115bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 0); 2125bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 2135bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 2146b57ff6fSAlison Wang 2156b57ff6fSAlison Wang /* Erase a sector */ 2166b57ff6fSAlison Wang lut_base = SEQID_SE * 4; 217a2358783SPeng Fan #ifdef CONFIG_SPI_FLASH_BAR 2185bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_SE) | 219a2358783SPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 220a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 221a2358783SPeng Fan #else 2226b57ff6fSAlison Wang if (FSL_QSPI_FLASH_SIZE <= SZ_16M) 2235bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 2245bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_SE) | PAD0(LUT_PAD1) | 2255bc48308SHaikun.Wang@freescale.com INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 2266b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 2276b57ff6fSAlison Wang else 2285bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 2295bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_SE_4B) | PAD0(LUT_PAD1) | 2305bc48308SHaikun.Wang@freescale.com INSTR0(LUT_CMD) | OPRND1(ADDR32BIT) | 2316b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 232a2358783SPeng Fan #endif 2335bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 0); 2345bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 2355bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 2366b57ff6fSAlison Wang 2376b57ff6fSAlison Wang /* Erase the whole chip */ 2386b57ff6fSAlison Wang lut_base = SEQID_CHIP_ERASE * 4; 2395bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 2405bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_CHIP_ERASE) | 2416b57ff6fSAlison Wang PAD0(LUT_PAD1) | INSTR0(LUT_CMD)); 2425bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 0); 2435bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 2445bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 2456b57ff6fSAlison Wang 2466b57ff6fSAlison Wang /* Page Program */ 2476b57ff6fSAlison Wang lut_base = SEQID_PP * 4; 248a2358783SPeng Fan #ifdef CONFIG_SPI_FLASH_BAR 2495bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_PP) | 250a2358783SPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 251a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 252a2358783SPeng Fan #else 2536b57ff6fSAlison Wang if (FSL_QSPI_FLASH_SIZE <= SZ_16M) 2545bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 2555bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_PP) | PAD0(LUT_PAD1) | 2565bc48308SHaikun.Wang@freescale.com INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 2576b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 2586b57ff6fSAlison Wang else 2595bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], 2605bc48308SHaikun.Wang@freescale.com OPRND0(QSPI_CMD_PP_4B) | PAD0(LUT_PAD1) | 2615bc48308SHaikun.Wang@freescale.com INSTR0(LUT_CMD) | OPRND1(ADDR32BIT) | 2626b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 263a2358783SPeng Fan #endif 264b93ab2eeSPeng Fan #ifdef CONFIG_MX6SX 265b93ab2eeSPeng Fan /* 266b93ab2eeSPeng Fan * To MX6SX, OPRND0(TX_BUFFER_SIZE) can not work correctly. 267b93ab2eeSPeng Fan * So, Use IDATSZ in IPCR to determine the size and here set 0. 268b93ab2eeSPeng Fan */ 2695bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], OPRND0(0) | 270b93ab2eeSPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)); 271b93ab2eeSPeng Fan #else 2725bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 2735bc48308SHaikun.Wang@freescale.com OPRND0(TX_BUFFER_SIZE) | 2746b57ff6fSAlison Wang PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)); 275b93ab2eeSPeng Fan #endif 2765bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 2775bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 2786b57ff6fSAlison Wang 2796b57ff6fSAlison Wang /* READ ID */ 2806b57ff6fSAlison Wang lut_base = SEQID_RDID * 4; 2815bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_RDID) | 2826b57ff6fSAlison Wang PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(8) | 2836b57ff6fSAlison Wang PAD1(LUT_PAD1) | INSTR1(LUT_READ)); 2845bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 1], 0); 2855bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); 2865bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); 2876b57ff6fSAlison Wang 288ba4dc8abSPeng Fan /* SUB SECTOR 4K ERASE */ 289ba4dc8abSPeng Fan lut_base = SEQID_BE_4K * 4; 2905bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_BE_4K) | 291ba4dc8abSPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | 292ba4dc8abSPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); 293ba4dc8abSPeng Fan 294a2358783SPeng Fan #ifdef CONFIG_SPI_FLASH_BAR 295a2358783SPeng Fan /* 296a2358783SPeng Fan * BRRD BRWR RDEAR WREAR are all supported, because it is hard to 297a2358783SPeng Fan * dynamically check whether to set BRRD BRWR or RDEAR WREAR during 298a2358783SPeng Fan * initialization. 299a2358783SPeng Fan */ 300a2358783SPeng Fan lut_base = SEQID_BRRD * 4; 3015bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_BRRD) | 302a2358783SPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | 303a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_READ)); 304a2358783SPeng Fan 305a2358783SPeng Fan lut_base = SEQID_BRWR * 4; 3065bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_BRWR) | 307a2358783SPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | 308a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_WRITE)); 309a2358783SPeng Fan 310a2358783SPeng Fan lut_base = SEQID_RDEAR * 4; 3115bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_RDEAR) | 312a2358783SPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | 313a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_READ)); 314a2358783SPeng Fan 315a2358783SPeng Fan lut_base = SEQID_WREAR * 4; 3165bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lut[lut_base], OPRND0(QSPI_CMD_WREAR) | 317a2358783SPeng Fan PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | 318a2358783SPeng Fan PAD1(LUT_PAD1) | INSTR1(LUT_WRITE)); 319a2358783SPeng Fan #endif 3206b57ff6fSAlison Wang /* Lock the LUT */ 3215bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lutkey, LUT_KEY_VALUE); 3225bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->lckcr, QSPI_LCKCR_LOCK); 3236b57ff6fSAlison Wang } 3246b57ff6fSAlison Wang 3255f7f70c1SPeng Fan #if defined(CONFIG_SYS_FSL_QSPI_AHB) 3265f7f70c1SPeng Fan /* 3275f7f70c1SPeng Fan * If we have changed the content of the flash by writing or erasing, 3285f7f70c1SPeng Fan * we need to invalidate the AHB buffer. If we do not do so, we may read out 3295f7f70c1SPeng Fan * the wrong data. The spec tells us reset the AHB domain and Serial Flash 3305f7f70c1SPeng Fan * domain at the same time. 3315f7f70c1SPeng Fan */ 3325bc48308SHaikun.Wang@freescale.com static inline void qspi_ahb_invalid(struct fsl_qspi_priv *priv) 3335f7f70c1SPeng Fan { 3345bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 3355f7f70c1SPeng Fan u32 reg; 3365f7f70c1SPeng Fan 3375bc48308SHaikun.Wang@freescale.com reg = qspi_read32(priv->flags, ®s->mcr); 3385f7f70c1SPeng Fan reg |= QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK; 3395bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, reg); 3405f7f70c1SPeng Fan 3415f7f70c1SPeng Fan /* 3425f7f70c1SPeng Fan * The minimum delay : 1 AHB + 2 SFCK clocks. 3435f7f70c1SPeng Fan * Delay 1 us is enough. 3445f7f70c1SPeng Fan */ 3455f7f70c1SPeng Fan udelay(1); 3465f7f70c1SPeng Fan 3475f7f70c1SPeng Fan reg &= ~(QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK); 3485bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, reg); 3495f7f70c1SPeng Fan } 3505f7f70c1SPeng Fan 3515f7f70c1SPeng Fan /* Read out the data from the AHB buffer. */ 3525bc48308SHaikun.Wang@freescale.com static inline void qspi_ahb_read(struct fsl_qspi_priv *priv, u8 *rxbuf, int len) 3535f7f70c1SPeng Fan { 3545bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 3555f7f70c1SPeng Fan u32 mcr_reg; 3565f7f70c1SPeng Fan 3575bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 3585f7f70c1SPeng Fan 3595bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 3605bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 3615f7f70c1SPeng Fan QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 3625f7f70c1SPeng Fan 3635f7f70c1SPeng Fan /* Read out the data directly from the AHB buffer. */ 3645bc48308SHaikun.Wang@freescale.com memcpy(rxbuf, (u8 *)(priv->cur_amba_base + priv->sf_addr), len); 3655f7f70c1SPeng Fan 3665bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 3675f7f70c1SPeng Fan } 3685f7f70c1SPeng Fan 3695bc48308SHaikun.Wang@freescale.com static void qspi_enable_ddr_mode(struct fsl_qspi_priv *priv) 3705f7f70c1SPeng Fan { 3715f7f70c1SPeng Fan u32 reg, reg2; 3725bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 3735f7f70c1SPeng Fan 3745bc48308SHaikun.Wang@freescale.com reg = qspi_read32(priv->flags, ®s->mcr); 3755f7f70c1SPeng Fan /* Disable the module */ 3765bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, reg | QSPI_MCR_MDIS_MASK); 3775f7f70c1SPeng Fan 3785f7f70c1SPeng Fan /* Set the Sampling Register for DDR */ 3795bc48308SHaikun.Wang@freescale.com reg2 = qspi_read32(priv->flags, ®s->smpr); 3805f7f70c1SPeng Fan reg2 &= ~QSPI_SMPR_DDRSMP_MASK; 3815f7f70c1SPeng Fan reg2 |= (2 << QSPI_SMPR_DDRSMP_SHIFT); 3825bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->smpr, reg2); 3835f7f70c1SPeng Fan 3845f7f70c1SPeng Fan /* Enable the module again (enable the DDR too) */ 3855f7f70c1SPeng Fan reg |= QSPI_MCR_DDR_EN_MASK; 3865f7f70c1SPeng Fan /* Enable bit 29 for imx6sx */ 38729e6abd9SJagan Teki reg |= BIT(29); 3885f7f70c1SPeng Fan 3895bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, reg); 3905f7f70c1SPeng Fan } 3915f7f70c1SPeng Fan 3925f7f70c1SPeng Fan /* 3935f7f70c1SPeng Fan * There are two different ways to read out the data from the flash: 3945f7f70c1SPeng Fan * the "IP Command Read" and the "AHB Command Read". 3955f7f70c1SPeng Fan * 3965f7f70c1SPeng Fan * The IC guy suggests we use the "AHB Command Read" which is faster 3975f7f70c1SPeng Fan * then the "IP Command Read". (What's more is that there is a bug in 3985f7f70c1SPeng Fan * the "IP Command Read" in the Vybrid.) 3995f7f70c1SPeng Fan * 4005f7f70c1SPeng Fan * After we set up the registers for the "AHB Command Read", we can use 4015f7f70c1SPeng Fan * the memcpy to read the data directly. A "missed" access to the buffer 4025f7f70c1SPeng Fan * causes the controller to clear the buffer, and use the sequence pointed 4035f7f70c1SPeng Fan * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. 4045f7f70c1SPeng Fan */ 4055bc48308SHaikun.Wang@freescale.com static void qspi_init_ahb_read(struct fsl_qspi_priv *priv) 4065f7f70c1SPeng Fan { 4075bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 4085bc48308SHaikun.Wang@freescale.com 4095f7f70c1SPeng Fan /* AHB configuration for access buffer 0/1/2 .*/ 4105bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf0cr, QSPI_BUFXCR_INVALID_MSTRID); 4115bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf1cr, QSPI_BUFXCR_INVALID_MSTRID); 4125bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf2cr, QSPI_BUFXCR_INVALID_MSTRID); 4135bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf3cr, QSPI_BUF3CR_ALLMST_MASK | 4145f7f70c1SPeng Fan (0x80 << QSPI_BUF3CR_ADATSZ_SHIFT)); 4155f7f70c1SPeng Fan 4165f7f70c1SPeng Fan /* We only use the buffer3 */ 4175bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf0ind, 0); 4185bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf1ind, 0); 4195bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->buf2ind, 0); 4205f7f70c1SPeng Fan 4215f7f70c1SPeng Fan /* 4225f7f70c1SPeng Fan * Set the default lut sequence for AHB Read. 4235f7f70c1SPeng Fan * Parallel mode is disabled. 4245f7f70c1SPeng Fan */ 4255bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->bfgencr, 4265f7f70c1SPeng Fan SEQID_FAST_READ << QSPI_BFGENCR_SEQID_SHIFT); 4275f7f70c1SPeng Fan 4285f7f70c1SPeng Fan /*Enable DDR Mode*/ 4295bc48308SHaikun.Wang@freescale.com qspi_enable_ddr_mode(priv); 4305f7f70c1SPeng Fan } 4315f7f70c1SPeng Fan #endif 4325f7f70c1SPeng Fan 4335bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SPI_FLASH_BAR 4345bc48308SHaikun.Wang@freescale.com /* Bank register read/write, EAR register read/write */ 4355bc48308SHaikun.Wang@freescale.com static void qspi_op_rdbank(struct fsl_qspi_priv *priv, u8 *rxbuf, u32 len) 4366b57ff6fSAlison Wang { 4375bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 4385bc48308SHaikun.Wang@freescale.com u32 reg, mcr_reg, data, seqid; 4395bc48308SHaikun.Wang@freescale.com 4405bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 4415bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 4425bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 4435bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 4445bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS); 4455bc48308SHaikun.Wang@freescale.com 4465bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->sfar, priv->cur_amba_base); 4475bc48308SHaikun.Wang@freescale.com 4485bc48308SHaikun.Wang@freescale.com if (priv->cur_seqid == QSPI_CMD_BRRD) 4495bc48308SHaikun.Wang@freescale.com seqid = SEQID_BRRD; 4505bc48308SHaikun.Wang@freescale.com else 4515bc48308SHaikun.Wang@freescale.com seqid = SEQID_RDEAR; 4525bc48308SHaikun.Wang@freescale.com 4535bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 4545bc48308SHaikun.Wang@freescale.com (seqid << QSPI_IPCR_SEQID_SHIFT) | len); 4555bc48308SHaikun.Wang@freescale.com 4565bc48308SHaikun.Wang@freescale.com /* Wait previous command complete */ 4575bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 4585bc48308SHaikun.Wang@freescale.com ; 4595bc48308SHaikun.Wang@freescale.com 4605bc48308SHaikun.Wang@freescale.com while (1) { 4615bc48308SHaikun.Wang@freescale.com reg = qspi_read32(priv->flags, ®s->rbsr); 4625bc48308SHaikun.Wang@freescale.com if (reg & QSPI_RBSR_RDBFL_MASK) { 4635bc48308SHaikun.Wang@freescale.com data = qspi_read32(priv->flags, ®s->rbdr[0]); 4645bc48308SHaikun.Wang@freescale.com data = qspi_endian_xchg(data); 4655bc48308SHaikun.Wang@freescale.com memcpy(rxbuf, &data, len); 4665bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 4675bc48308SHaikun.Wang@freescale.com qspi_read32(priv->flags, ®s->mcr) | 4685bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK); 4695bc48308SHaikun.Wang@freescale.com break; 4705bc48308SHaikun.Wang@freescale.com } 4715bc48308SHaikun.Wang@freescale.com } 4725bc48308SHaikun.Wang@freescale.com 4735bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 4745bc48308SHaikun.Wang@freescale.com } 4755bc48308SHaikun.Wang@freescale.com #endif 4765bc48308SHaikun.Wang@freescale.com 4775bc48308SHaikun.Wang@freescale.com static void qspi_op_rdid(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len) 4785bc48308SHaikun.Wang@freescale.com { 4795bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 4805207014dSGong Qianyu u32 mcr_reg, rbsr_reg, data, size; 4815207014dSGong Qianyu int i; 4825bc48308SHaikun.Wang@freescale.com 4835bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 4845bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 4855bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 4865bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 4875bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS); 4885bc48308SHaikun.Wang@freescale.com 4895bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->sfar, priv->cur_amba_base); 4905bc48308SHaikun.Wang@freescale.com 4915bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 4925bc48308SHaikun.Wang@freescale.com (SEQID_RDID << QSPI_IPCR_SEQID_SHIFT) | 0); 4935bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 4945bc48308SHaikun.Wang@freescale.com ; 4955bc48308SHaikun.Wang@freescale.com 4965bc48308SHaikun.Wang@freescale.com i = 0; 4975207014dSGong Qianyu while ((RX_BUFFER_SIZE >= len) && (len > 0)) { 4985bc48308SHaikun.Wang@freescale.com rbsr_reg = qspi_read32(priv->flags, ®s->rbsr); 4995bc48308SHaikun.Wang@freescale.com if (rbsr_reg & QSPI_RBSR_RDBFL_MASK) { 5005bc48308SHaikun.Wang@freescale.com data = qspi_read32(priv->flags, ®s->rbdr[i]); 5015bc48308SHaikun.Wang@freescale.com data = qspi_endian_xchg(data); 5025207014dSGong Qianyu size = (len < 4) ? len : 4; 5035207014dSGong Qianyu memcpy(rxbuf, &data, size); 5045207014dSGong Qianyu len -= size; 5055bc48308SHaikun.Wang@freescale.com rxbuf++; 5065bc48308SHaikun.Wang@freescale.com i++; 5075bc48308SHaikun.Wang@freescale.com } 5085bc48308SHaikun.Wang@freescale.com } 5095bc48308SHaikun.Wang@freescale.com 5105bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 5115bc48308SHaikun.Wang@freescale.com } 5125bc48308SHaikun.Wang@freescale.com 5135bc48308SHaikun.Wang@freescale.com #ifndef CONFIG_SYS_FSL_QSPI_AHB 5145bc48308SHaikun.Wang@freescale.com /* If not use AHB read, read data from ip interface */ 5155bc48308SHaikun.Wang@freescale.com static void qspi_op_read(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len) 5165bc48308SHaikun.Wang@freescale.com { 5175bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 5185bc48308SHaikun.Wang@freescale.com u32 mcr_reg, data; 5195bc48308SHaikun.Wang@freescale.com int i, size; 5205bc48308SHaikun.Wang@freescale.com u32 to_or_from; 5215bc48308SHaikun.Wang@freescale.com 5225bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 5235bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 5245bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 5255bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 5265bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS); 5275bc48308SHaikun.Wang@freescale.com 5285bc48308SHaikun.Wang@freescale.com to_or_from = priv->sf_addr + priv->cur_amba_base; 5295bc48308SHaikun.Wang@freescale.com 5305bc48308SHaikun.Wang@freescale.com while (len > 0) { 531beedbc2eSAlexander Stein WATCHDOG_RESET(); 532beedbc2eSAlexander Stein 5335bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->sfar, to_or_from); 5345bc48308SHaikun.Wang@freescale.com 5355bc48308SHaikun.Wang@freescale.com size = (len > RX_BUFFER_SIZE) ? 5365bc48308SHaikun.Wang@freescale.com RX_BUFFER_SIZE : len; 5375bc48308SHaikun.Wang@freescale.com 5385bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 5395bc48308SHaikun.Wang@freescale.com (SEQID_FAST_READ << QSPI_IPCR_SEQID_SHIFT) | 5405bc48308SHaikun.Wang@freescale.com size); 5415bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 5425bc48308SHaikun.Wang@freescale.com ; 5435bc48308SHaikun.Wang@freescale.com 5445bc48308SHaikun.Wang@freescale.com to_or_from += size; 5455bc48308SHaikun.Wang@freescale.com len -= size; 5465bc48308SHaikun.Wang@freescale.com 5475bc48308SHaikun.Wang@freescale.com i = 0; 5485bc48308SHaikun.Wang@freescale.com while ((RX_BUFFER_SIZE >= size) && (size > 0)) { 5495bc48308SHaikun.Wang@freescale.com data = qspi_read32(priv->flags, ®s->rbdr[i]); 5505bc48308SHaikun.Wang@freescale.com data = qspi_endian_xchg(data); 5515bc48308SHaikun.Wang@freescale.com memcpy(rxbuf, &data, 4); 5525bc48308SHaikun.Wang@freescale.com rxbuf++; 5535bc48308SHaikun.Wang@freescale.com size -= 4; 5545bc48308SHaikun.Wang@freescale.com i++; 5555bc48308SHaikun.Wang@freescale.com } 5565bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 5575bc48308SHaikun.Wang@freescale.com qspi_read32(priv->flags, ®s->mcr) | 5585bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK); 5595bc48308SHaikun.Wang@freescale.com } 5605bc48308SHaikun.Wang@freescale.com 5615bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 5625bc48308SHaikun.Wang@freescale.com } 5635bc48308SHaikun.Wang@freescale.com #endif 5645bc48308SHaikun.Wang@freescale.com 5655bc48308SHaikun.Wang@freescale.com static void qspi_op_write(struct fsl_qspi_priv *priv, u8 *txbuf, u32 len) 5665bc48308SHaikun.Wang@freescale.com { 5675bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 5685bc48308SHaikun.Wang@freescale.com u32 mcr_reg, data, reg, status_reg, seqid; 5695bc48308SHaikun.Wang@freescale.com int i, size, tx_size; 5705bc48308SHaikun.Wang@freescale.com u32 to_or_from = 0; 5715bc48308SHaikun.Wang@freescale.com 5725bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 5735bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 5745bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 5755bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 5765bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS); 5775bc48308SHaikun.Wang@freescale.com 5785bc48308SHaikun.Wang@freescale.com status_reg = 0; 5795bc48308SHaikun.Wang@freescale.com while ((status_reg & FLASH_STATUS_WEL) != FLASH_STATUS_WEL) { 580beedbc2eSAlexander Stein WATCHDOG_RESET(); 581beedbc2eSAlexander Stein 5825bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 5835bc48308SHaikun.Wang@freescale.com (SEQID_WREN << QSPI_IPCR_SEQID_SHIFT) | 0); 5845bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 5855bc48308SHaikun.Wang@freescale.com ; 5865bc48308SHaikun.Wang@freescale.com 5875bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 5885bc48308SHaikun.Wang@freescale.com (SEQID_RDSR << QSPI_IPCR_SEQID_SHIFT) | 1); 5895bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 5905bc48308SHaikun.Wang@freescale.com ; 5915bc48308SHaikun.Wang@freescale.com 5925bc48308SHaikun.Wang@freescale.com reg = qspi_read32(priv->flags, ®s->rbsr); 5935bc48308SHaikun.Wang@freescale.com if (reg & QSPI_RBSR_RDBFL_MASK) { 5945bc48308SHaikun.Wang@freescale.com status_reg = qspi_read32(priv->flags, ®s->rbdr[0]); 5955bc48308SHaikun.Wang@freescale.com status_reg = qspi_endian_xchg(status_reg); 5965bc48308SHaikun.Wang@freescale.com } 5975bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 5985bc48308SHaikun.Wang@freescale.com qspi_read32(priv->flags, ®s->mcr) | 5995bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK); 6005bc48308SHaikun.Wang@freescale.com } 6015bc48308SHaikun.Wang@freescale.com 6025bc48308SHaikun.Wang@freescale.com /* Default is page programming */ 6035bc48308SHaikun.Wang@freescale.com seqid = SEQID_PP; 6045bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SPI_FLASH_BAR 6055bc48308SHaikun.Wang@freescale.com if (priv->cur_seqid == QSPI_CMD_BRWR) 6065bc48308SHaikun.Wang@freescale.com seqid = SEQID_BRWR; 6075bc48308SHaikun.Wang@freescale.com else if (priv->cur_seqid == QSPI_CMD_WREAR) 6085bc48308SHaikun.Wang@freescale.com seqid = SEQID_WREAR; 6095bc48308SHaikun.Wang@freescale.com #endif 6105bc48308SHaikun.Wang@freescale.com 6115bc48308SHaikun.Wang@freescale.com to_or_from = priv->sf_addr + priv->cur_amba_base; 6125bc48308SHaikun.Wang@freescale.com 6135bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->sfar, to_or_from); 6145bc48308SHaikun.Wang@freescale.com 6155bc48308SHaikun.Wang@freescale.com tx_size = (len > TX_BUFFER_SIZE) ? 6165bc48308SHaikun.Wang@freescale.com TX_BUFFER_SIZE : len; 6175bc48308SHaikun.Wang@freescale.com 6185bc48308SHaikun.Wang@freescale.com size = tx_size / 4; 6195bc48308SHaikun.Wang@freescale.com for (i = 0; i < size; i++) { 6205bc48308SHaikun.Wang@freescale.com memcpy(&data, txbuf, 4); 6215bc48308SHaikun.Wang@freescale.com data = qspi_endian_xchg(data); 6225bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->tbdr, data); 6235bc48308SHaikun.Wang@freescale.com txbuf += 4; 6245bc48308SHaikun.Wang@freescale.com } 6255bc48308SHaikun.Wang@freescale.com 6265bc48308SHaikun.Wang@freescale.com size = tx_size % 4; 6275bc48308SHaikun.Wang@freescale.com if (size) { 6285bc48308SHaikun.Wang@freescale.com data = 0; 6295bc48308SHaikun.Wang@freescale.com memcpy(&data, txbuf, size); 6305bc48308SHaikun.Wang@freescale.com data = qspi_endian_xchg(data); 6315bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->tbdr, data); 6325bc48308SHaikun.Wang@freescale.com } 6335bc48308SHaikun.Wang@freescale.com 6345bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 6355bc48308SHaikun.Wang@freescale.com (seqid << QSPI_IPCR_SEQID_SHIFT) | tx_size); 6365bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 6375bc48308SHaikun.Wang@freescale.com ; 6385bc48308SHaikun.Wang@freescale.com 6395bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 6405bc48308SHaikun.Wang@freescale.com } 6415bc48308SHaikun.Wang@freescale.com 642*940d2b89SGong Qianyu static void qspi_op_rdsr(struct fsl_qspi_priv *priv, void *rxbuf, u32 len) 6435bc48308SHaikun.Wang@freescale.com { 6445bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 6455bc48308SHaikun.Wang@freescale.com u32 mcr_reg, reg, data; 6465bc48308SHaikun.Wang@freescale.com 6475bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 6485bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 6495bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 6505bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 6515bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS); 6525bc48308SHaikun.Wang@freescale.com 6535bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->sfar, priv->cur_amba_base); 6545bc48308SHaikun.Wang@freescale.com 6555bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 6565bc48308SHaikun.Wang@freescale.com (SEQID_RDSR << QSPI_IPCR_SEQID_SHIFT) | 0); 6575bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 6585bc48308SHaikun.Wang@freescale.com ; 6595bc48308SHaikun.Wang@freescale.com 6605bc48308SHaikun.Wang@freescale.com while (1) { 6615bc48308SHaikun.Wang@freescale.com reg = qspi_read32(priv->flags, ®s->rbsr); 6625bc48308SHaikun.Wang@freescale.com if (reg & QSPI_RBSR_RDBFL_MASK) { 6635bc48308SHaikun.Wang@freescale.com data = qspi_read32(priv->flags, ®s->rbdr[0]); 6645bc48308SHaikun.Wang@freescale.com data = qspi_endian_xchg(data); 665*940d2b89SGong Qianyu memcpy(rxbuf, &data, len); 6665bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 6675bc48308SHaikun.Wang@freescale.com qspi_read32(priv->flags, ®s->mcr) | 6685bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK); 6695bc48308SHaikun.Wang@freescale.com break; 6705bc48308SHaikun.Wang@freescale.com } 6715bc48308SHaikun.Wang@freescale.com } 6725bc48308SHaikun.Wang@freescale.com 6735bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 6745bc48308SHaikun.Wang@freescale.com } 6755bc48308SHaikun.Wang@freescale.com 6765bc48308SHaikun.Wang@freescale.com static void qspi_op_erase(struct fsl_qspi_priv *priv) 6775bc48308SHaikun.Wang@freescale.com { 6785bc48308SHaikun.Wang@freescale.com struct fsl_qspi_regs *regs = priv->regs; 6795bc48308SHaikun.Wang@freescale.com u32 mcr_reg; 6805bc48308SHaikun.Wang@freescale.com u32 to_or_from = 0; 6815bc48308SHaikun.Wang@freescale.com 6825bc48308SHaikun.Wang@freescale.com mcr_reg = qspi_read32(priv->flags, ®s->mcr); 6835bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, 6845bc48308SHaikun.Wang@freescale.com QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 6855bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); 6865bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS); 6875bc48308SHaikun.Wang@freescale.com 6885bc48308SHaikun.Wang@freescale.com to_or_from = priv->sf_addr + priv->cur_amba_base; 6895bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->sfar, to_or_from); 6905bc48308SHaikun.Wang@freescale.com 6915bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 6925bc48308SHaikun.Wang@freescale.com (SEQID_WREN << QSPI_IPCR_SEQID_SHIFT) | 0); 6935bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 6945bc48308SHaikun.Wang@freescale.com ; 6955bc48308SHaikun.Wang@freescale.com 6965bc48308SHaikun.Wang@freescale.com if (priv->cur_seqid == QSPI_CMD_SE) { 6975bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 6985bc48308SHaikun.Wang@freescale.com (SEQID_SE << QSPI_IPCR_SEQID_SHIFT) | 0); 6995bc48308SHaikun.Wang@freescale.com } else if (priv->cur_seqid == QSPI_CMD_BE_4K) { 7005bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->ipcr, 7015bc48308SHaikun.Wang@freescale.com (SEQID_BE_4K << QSPI_IPCR_SEQID_SHIFT) | 0); 7025bc48308SHaikun.Wang@freescale.com } 7035bc48308SHaikun.Wang@freescale.com while (qspi_read32(priv->flags, ®s->sr) & QSPI_SR_BUSY_MASK) 7045bc48308SHaikun.Wang@freescale.com ; 7055bc48308SHaikun.Wang@freescale.com 7065bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, ®s->mcr, mcr_reg); 7075bc48308SHaikun.Wang@freescale.com } 7085bc48308SHaikun.Wang@freescale.com 7095bc48308SHaikun.Wang@freescale.com int qspi_xfer(struct fsl_qspi_priv *priv, unsigned int bitlen, 7105bc48308SHaikun.Wang@freescale.com const void *dout, void *din, unsigned long flags) 7115bc48308SHaikun.Wang@freescale.com { 7125bc48308SHaikun.Wang@freescale.com u32 bytes = DIV_ROUND_UP(bitlen, 8); 7135bc48308SHaikun.Wang@freescale.com static u32 wr_sfaddr; 7145bc48308SHaikun.Wang@freescale.com u32 txbuf; 7155bc48308SHaikun.Wang@freescale.com 7165bc48308SHaikun.Wang@freescale.com if (dout) { 7175bc48308SHaikun.Wang@freescale.com if (flags & SPI_XFER_BEGIN) { 7185bc48308SHaikun.Wang@freescale.com priv->cur_seqid = *(u8 *)dout; 7195bc48308SHaikun.Wang@freescale.com memcpy(&txbuf, dout, 4); 7205bc48308SHaikun.Wang@freescale.com } 7215bc48308SHaikun.Wang@freescale.com 7225bc48308SHaikun.Wang@freescale.com if (flags == SPI_XFER_END) { 7235bc48308SHaikun.Wang@freescale.com priv->sf_addr = wr_sfaddr; 7245bc48308SHaikun.Wang@freescale.com qspi_op_write(priv, (u8 *)dout, bytes); 7255bc48308SHaikun.Wang@freescale.com return 0; 7265bc48308SHaikun.Wang@freescale.com } 7275bc48308SHaikun.Wang@freescale.com 7285bc48308SHaikun.Wang@freescale.com if (priv->cur_seqid == QSPI_CMD_FAST_READ) { 7295bc48308SHaikun.Wang@freescale.com priv->sf_addr = swab32(txbuf) & OFFSET_BITS_MASK; 7305bc48308SHaikun.Wang@freescale.com } else if ((priv->cur_seqid == QSPI_CMD_SE) || 7315bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_BE_4K)) { 7325bc48308SHaikun.Wang@freescale.com priv->sf_addr = swab32(txbuf) & OFFSET_BITS_MASK; 7335bc48308SHaikun.Wang@freescale.com qspi_op_erase(priv); 7345bc48308SHaikun.Wang@freescale.com } else if (priv->cur_seqid == QSPI_CMD_PP) { 7355bc48308SHaikun.Wang@freescale.com wr_sfaddr = swab32(txbuf) & OFFSET_BITS_MASK; 7365bc48308SHaikun.Wang@freescale.com } else if ((priv->cur_seqid == QSPI_CMD_BRWR) || 7375bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_WREAR)) { 7385bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SPI_FLASH_BAR 7395bc48308SHaikun.Wang@freescale.com wr_sfaddr = 0; 7405bc48308SHaikun.Wang@freescale.com #endif 7415bc48308SHaikun.Wang@freescale.com } 7425bc48308SHaikun.Wang@freescale.com } 7435bc48308SHaikun.Wang@freescale.com 7445bc48308SHaikun.Wang@freescale.com if (din) { 7455bc48308SHaikun.Wang@freescale.com if (priv->cur_seqid == QSPI_CMD_FAST_READ) { 7465bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SYS_FSL_QSPI_AHB 7475bc48308SHaikun.Wang@freescale.com qspi_ahb_read(priv, din, bytes); 7485bc48308SHaikun.Wang@freescale.com #else 7495bc48308SHaikun.Wang@freescale.com qspi_op_read(priv, din, bytes); 7505bc48308SHaikun.Wang@freescale.com #endif 7515bc48308SHaikun.Wang@freescale.com } else if (priv->cur_seqid == QSPI_CMD_RDID) 7525bc48308SHaikun.Wang@freescale.com qspi_op_rdid(priv, din, bytes); 7535bc48308SHaikun.Wang@freescale.com else if (priv->cur_seqid == QSPI_CMD_RDSR) 754*940d2b89SGong Qianyu qspi_op_rdsr(priv, din, bytes); 7555bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SPI_FLASH_BAR 7565bc48308SHaikun.Wang@freescale.com else if ((priv->cur_seqid == QSPI_CMD_BRRD) || 7575bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_RDEAR)) { 7585bc48308SHaikun.Wang@freescale.com priv->sf_addr = 0; 7595bc48308SHaikun.Wang@freescale.com qspi_op_rdbank(priv, din, bytes); 7605bc48308SHaikun.Wang@freescale.com } 7615bc48308SHaikun.Wang@freescale.com #endif 7625bc48308SHaikun.Wang@freescale.com } 7635bc48308SHaikun.Wang@freescale.com 7645bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SYS_FSL_QSPI_AHB 7655bc48308SHaikun.Wang@freescale.com if ((priv->cur_seqid == QSPI_CMD_SE) || 7665bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_PP) || 7675bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_BE_4K) || 7685bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_WREAR) || 7695bc48308SHaikun.Wang@freescale.com (priv->cur_seqid == QSPI_CMD_BRWR)) 7705bc48308SHaikun.Wang@freescale.com qspi_ahb_invalid(priv); 7715bc48308SHaikun.Wang@freescale.com #endif 7725bc48308SHaikun.Wang@freescale.com 7735bc48308SHaikun.Wang@freescale.com return 0; 7745bc48308SHaikun.Wang@freescale.com } 7755bc48308SHaikun.Wang@freescale.com 7765bc48308SHaikun.Wang@freescale.com void qspi_module_disable(struct fsl_qspi_priv *priv, u8 disable) 7775bc48308SHaikun.Wang@freescale.com { 7785bc48308SHaikun.Wang@freescale.com u32 mcr_val; 7795bc48308SHaikun.Wang@freescale.com 7805bc48308SHaikun.Wang@freescale.com mcr_val = qspi_read32(priv->flags, &priv->regs->mcr); 7815bc48308SHaikun.Wang@freescale.com if (disable) 7825bc48308SHaikun.Wang@freescale.com mcr_val |= QSPI_MCR_MDIS_MASK; 7835bc48308SHaikun.Wang@freescale.com else 7845bc48308SHaikun.Wang@freescale.com mcr_val &= ~QSPI_MCR_MDIS_MASK; 7855bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->mcr, mcr_val); 7865bc48308SHaikun.Wang@freescale.com } 7875bc48308SHaikun.Wang@freescale.com 7885bc48308SHaikun.Wang@freescale.com void qspi_cfg_smpr(struct fsl_qspi_priv *priv, u32 clear_bits, u32 set_bits) 7895bc48308SHaikun.Wang@freescale.com { 7905bc48308SHaikun.Wang@freescale.com u32 smpr_val; 7915bc48308SHaikun.Wang@freescale.com 7925bc48308SHaikun.Wang@freescale.com smpr_val = qspi_read32(priv->flags, &priv->regs->smpr); 7935bc48308SHaikun.Wang@freescale.com smpr_val &= ~clear_bits; 7945bc48308SHaikun.Wang@freescale.com smpr_val |= set_bits; 7955bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->smpr, smpr_val); 7965bc48308SHaikun.Wang@freescale.com } 7975bc48308SHaikun.Wang@freescale.com #ifndef CONFIG_DM_SPI 7985bc48308SHaikun.Wang@freescale.com static unsigned long spi_bases[] = { 7995bc48308SHaikun.Wang@freescale.com QSPI0_BASE_ADDR, 8005bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_MX6SX 8015bc48308SHaikun.Wang@freescale.com QSPI1_BASE_ADDR, 8025bc48308SHaikun.Wang@freescale.com #endif 8035bc48308SHaikun.Wang@freescale.com }; 8045bc48308SHaikun.Wang@freescale.com 8055bc48308SHaikun.Wang@freescale.com static unsigned long amba_bases[] = { 8065bc48308SHaikun.Wang@freescale.com QSPI0_AMBA_BASE, 8075bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_MX6SX 8085bc48308SHaikun.Wang@freescale.com QSPI1_AMBA_BASE, 8095bc48308SHaikun.Wang@freescale.com #endif 8105bc48308SHaikun.Wang@freescale.com }; 8115bc48308SHaikun.Wang@freescale.com 8125bc48308SHaikun.Wang@freescale.com static inline struct fsl_qspi *to_qspi_spi(struct spi_slave *slave) 8135bc48308SHaikun.Wang@freescale.com { 8145bc48308SHaikun.Wang@freescale.com return container_of(slave, struct fsl_qspi, slave); 8156b57ff6fSAlison Wang } 8166b57ff6fSAlison Wang 8176b57ff6fSAlison Wang struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, 8186b57ff6fSAlison Wang unsigned int max_hz, unsigned int mode) 8196b57ff6fSAlison Wang { 8206b57ff6fSAlison Wang struct fsl_qspi *qspi; 8216b57ff6fSAlison Wang struct fsl_qspi_regs *regs; 8225f7f70c1SPeng Fan u32 total_size; 8236b57ff6fSAlison Wang 8246b57ff6fSAlison Wang if (bus >= ARRAY_SIZE(spi_bases)) 8256b57ff6fSAlison Wang return NULL; 8266b57ff6fSAlison Wang 827ed0c81c6SPeng Fan if (cs >= FSL_QSPI_FLASH_NUM) 828ed0c81c6SPeng Fan return NULL; 829ed0c81c6SPeng Fan 8306b57ff6fSAlison Wang qspi = spi_alloc_slave(struct fsl_qspi, bus, cs); 8316b57ff6fSAlison Wang if (!qspi) 8326b57ff6fSAlison Wang return NULL; 8336b57ff6fSAlison Wang 8345bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SYS_FSL_QSPI_BE 8355bc48308SHaikun.Wang@freescale.com qspi->priv.flags |= QSPI_FLAG_REGMAP_ENDIAN_BIG; 8365bc48308SHaikun.Wang@freescale.com #endif 8375bc48308SHaikun.Wang@freescale.com 8385bc48308SHaikun.Wang@freescale.com regs = (struct fsl_qspi_regs *)spi_bases[bus]; 8395bc48308SHaikun.Wang@freescale.com qspi->priv.regs = regs; 840ed0c81c6SPeng Fan /* 841ed0c81c6SPeng Fan * According cs, use different amba_base to choose the 842ed0c81c6SPeng Fan * corresponding flash devices. 843ed0c81c6SPeng Fan * 844ed0c81c6SPeng Fan * If not, only one flash device is used even if passing 845ed0c81c6SPeng Fan * different cs using `sf probe` 846ed0c81c6SPeng Fan */ 8475bc48308SHaikun.Wang@freescale.com qspi->priv.cur_amba_base = amba_bases[bus] + cs * FSL_QSPI_FLASH_SIZE; 8486b57ff6fSAlison Wang 8496b57ff6fSAlison Wang qspi->slave.max_write_size = TX_BUFFER_SIZE; 8506b57ff6fSAlison Wang 8515bc48308SHaikun.Wang@freescale.com qspi_write32(qspi->priv.flags, ®s->mcr, 8525bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK); 8536b57ff6fSAlison Wang 8545bc48308SHaikun.Wang@freescale.com qspi_cfg_smpr(&qspi->priv, 8555bc48308SHaikun.Wang@freescale.com ~(QSPI_SMPR_FSDLY_MASK | QSPI_SMPR_DDRSMP_MASK | 8565bc48308SHaikun.Wang@freescale.com QSPI_SMPR_FSPHS_MASK | QSPI_SMPR_HSENA_MASK), 0); 8576b57ff6fSAlison Wang 8586b57ff6fSAlison Wang total_size = FSL_QSPI_FLASH_SIZE * FSL_QSPI_FLASH_NUM; 859ed0c81c6SPeng Fan /* 860ed0c81c6SPeng Fan * Any read access to non-implemented addresses will provide 861ed0c81c6SPeng Fan * undefined results. 862ed0c81c6SPeng Fan * 863ed0c81c6SPeng Fan * In case single die flash devices, TOP_ADDR_MEMA2 and 864ed0c81c6SPeng Fan * TOP_ADDR_MEMB2 should be initialized/programmed to 865ed0c81c6SPeng Fan * TOP_ADDR_MEMA1 and TOP_ADDR_MEMB1 respectively - in effect, 866ed0c81c6SPeng Fan * setting the size of these devices to 0. This would ensure 867ed0c81c6SPeng Fan * that the complete memory map is assigned to only one flash device. 868ed0c81c6SPeng Fan */ 8695bc48308SHaikun.Wang@freescale.com qspi_write32(qspi->priv.flags, ®s->sfa1ad, 8705bc48308SHaikun.Wang@freescale.com FSL_QSPI_FLASH_SIZE | amba_bases[bus]); 8715bc48308SHaikun.Wang@freescale.com qspi_write32(qspi->priv.flags, ®s->sfa2ad, 8725bc48308SHaikun.Wang@freescale.com FSL_QSPI_FLASH_SIZE | amba_bases[bus]); 8735bc48308SHaikun.Wang@freescale.com qspi_write32(qspi->priv.flags, ®s->sfb1ad, 8745bc48308SHaikun.Wang@freescale.com total_size | amba_bases[bus]); 8755bc48308SHaikun.Wang@freescale.com qspi_write32(qspi->priv.flags, ®s->sfb2ad, 8765bc48308SHaikun.Wang@freescale.com total_size | amba_bases[bus]); 8776b57ff6fSAlison Wang 8785bc48308SHaikun.Wang@freescale.com qspi_set_lut(&qspi->priv); 8796b57ff6fSAlison Wang 8805f7f70c1SPeng Fan #ifdef CONFIG_SYS_FSL_QSPI_AHB 8815bc48308SHaikun.Wang@freescale.com qspi_init_ahb_read(&qspi->priv); 8825f7f70c1SPeng Fan #endif 8835bc48308SHaikun.Wang@freescale.com 8845bc48308SHaikun.Wang@freescale.com qspi_module_disable(&qspi->priv, 0); 8855bc48308SHaikun.Wang@freescale.com 8866b57ff6fSAlison Wang return &qspi->slave; 8876b57ff6fSAlison Wang } 8886b57ff6fSAlison Wang 8896b57ff6fSAlison Wang void spi_free_slave(struct spi_slave *slave) 8906b57ff6fSAlison Wang { 8916b57ff6fSAlison Wang struct fsl_qspi *qspi = to_qspi_spi(slave); 8926b57ff6fSAlison Wang 8936b57ff6fSAlison Wang free(qspi); 8946b57ff6fSAlison Wang } 8956b57ff6fSAlison Wang 8966b57ff6fSAlison Wang int spi_claim_bus(struct spi_slave *slave) 8976b57ff6fSAlison Wang { 8986b57ff6fSAlison Wang return 0; 8996b57ff6fSAlison Wang } 9006b57ff6fSAlison Wang 9015bc48308SHaikun.Wang@freescale.com void spi_release_bus(struct spi_slave *slave) 902a2358783SPeng Fan { 9035bc48308SHaikun.Wang@freescale.com /* Nothing to do */ 9046b57ff6fSAlison Wang } 9056b57ff6fSAlison Wang 9066b57ff6fSAlison Wang int spi_xfer(struct spi_slave *slave, unsigned int bitlen, 9076b57ff6fSAlison Wang const void *dout, void *din, unsigned long flags) 9086b57ff6fSAlison Wang { 9096b57ff6fSAlison Wang struct fsl_qspi *qspi = to_qspi_spi(slave); 9106b57ff6fSAlison Wang 9115bc48308SHaikun.Wang@freescale.com return qspi_xfer(&qspi->priv, bitlen, dout, din, flags); 912a2358783SPeng Fan } 9136b57ff6fSAlison Wang 9145bc48308SHaikun.Wang@freescale.com void spi_init(void) 9156b57ff6fSAlison Wang { 9166b57ff6fSAlison Wang /* Nothing to do */ 9176b57ff6fSAlison Wang } 9185bc48308SHaikun.Wang@freescale.com #else 9195bc48308SHaikun.Wang@freescale.com static int fsl_qspi_child_pre_probe(struct udevice *dev) 9205bc48308SHaikun.Wang@freescale.com { 921bcbe3d15SSimon Glass struct spi_slave *slave = dev_get_parent_priv(dev); 9225bc48308SHaikun.Wang@freescale.com 9235bc48308SHaikun.Wang@freescale.com slave->max_write_size = TX_BUFFER_SIZE; 9245bc48308SHaikun.Wang@freescale.com 9255bc48308SHaikun.Wang@freescale.com return 0; 9265bc48308SHaikun.Wang@freescale.com } 9275bc48308SHaikun.Wang@freescale.com 9285bc48308SHaikun.Wang@freescale.com static int fsl_qspi_probe(struct udevice *bus) 9295bc48308SHaikun.Wang@freescale.com { 9305bc48308SHaikun.Wang@freescale.com u32 total_size; 9315bc48308SHaikun.Wang@freescale.com struct fsl_qspi_platdata *plat = dev_get_platdata(bus); 9325bc48308SHaikun.Wang@freescale.com struct fsl_qspi_priv *priv = dev_get_priv(bus); 9335bc48308SHaikun.Wang@freescale.com struct dm_spi_bus *dm_spi_bus; 9345bc48308SHaikun.Wang@freescale.com 9355bc48308SHaikun.Wang@freescale.com dm_spi_bus = bus->uclass_priv; 9365bc48308SHaikun.Wang@freescale.com 9375bc48308SHaikun.Wang@freescale.com dm_spi_bus->max_hz = plat->speed_hz; 9385bc48308SHaikun.Wang@freescale.com 939c2a4cb17SGong Qianyu priv->regs = (struct fsl_qspi_regs *)(uintptr_t)plat->reg_base; 9405bc48308SHaikun.Wang@freescale.com priv->flags = plat->flags; 9415bc48308SHaikun.Wang@freescale.com 9425bc48308SHaikun.Wang@freescale.com priv->speed_hz = plat->speed_hz; 9435bc48308SHaikun.Wang@freescale.com priv->amba_base[0] = plat->amba_base; 9445bc48308SHaikun.Wang@freescale.com priv->amba_total_size = plat->amba_total_size; 9455bc48308SHaikun.Wang@freescale.com priv->flash_num = plat->flash_num; 9465bc48308SHaikun.Wang@freescale.com priv->num_chipselect = plat->num_chipselect; 9475bc48308SHaikun.Wang@freescale.com 9485bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->mcr, 9495bc48308SHaikun.Wang@freescale.com QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK); 9505bc48308SHaikun.Wang@freescale.com 9515bc48308SHaikun.Wang@freescale.com qspi_cfg_smpr(priv, ~(QSPI_SMPR_FSDLY_MASK | QSPI_SMPR_DDRSMP_MASK | 9525bc48308SHaikun.Wang@freescale.com QSPI_SMPR_FSPHS_MASK | QSPI_SMPR_HSENA_MASK), 0); 9535bc48308SHaikun.Wang@freescale.com 9545bc48308SHaikun.Wang@freescale.com total_size = FSL_QSPI_FLASH_SIZE * FSL_QSPI_FLASH_NUM; 9555bc48308SHaikun.Wang@freescale.com /* 9565bc48308SHaikun.Wang@freescale.com * Any read access to non-implemented addresses will provide 9575bc48308SHaikun.Wang@freescale.com * undefined results. 9585bc48308SHaikun.Wang@freescale.com * 9595bc48308SHaikun.Wang@freescale.com * In case single die flash devices, TOP_ADDR_MEMA2 and 9605bc48308SHaikun.Wang@freescale.com * TOP_ADDR_MEMB2 should be initialized/programmed to 9615bc48308SHaikun.Wang@freescale.com * TOP_ADDR_MEMA1 and TOP_ADDR_MEMB1 respectively - in effect, 9625bc48308SHaikun.Wang@freescale.com * setting the size of these devices to 0. This would ensure 9635bc48308SHaikun.Wang@freescale.com * that the complete memory map is assigned to only one flash device. 9645bc48308SHaikun.Wang@freescale.com */ 9655bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->sfa1ad, 9665bc48308SHaikun.Wang@freescale.com FSL_QSPI_FLASH_SIZE | priv->amba_base[0]); 9675bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->sfa2ad, 9685bc48308SHaikun.Wang@freescale.com FSL_QSPI_FLASH_SIZE | priv->amba_base[0]); 9695bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->sfb1ad, 9705bc48308SHaikun.Wang@freescale.com total_size | priv->amba_base[0]); 9715bc48308SHaikun.Wang@freescale.com qspi_write32(priv->flags, &priv->regs->sfb2ad, 9725bc48308SHaikun.Wang@freescale.com total_size | priv->amba_base[0]); 9735bc48308SHaikun.Wang@freescale.com 9745bc48308SHaikun.Wang@freescale.com qspi_set_lut(priv); 9755bc48308SHaikun.Wang@freescale.com 9765bc48308SHaikun.Wang@freescale.com #ifdef CONFIG_SYS_FSL_QSPI_AHB 9775bc48308SHaikun.Wang@freescale.com qspi_init_ahb_read(priv); 9785bc48308SHaikun.Wang@freescale.com #endif 9795bc48308SHaikun.Wang@freescale.com 9805bc48308SHaikun.Wang@freescale.com qspi_module_disable(priv, 0); 9815bc48308SHaikun.Wang@freescale.com 9825bc48308SHaikun.Wang@freescale.com return 0; 9835bc48308SHaikun.Wang@freescale.com } 9845bc48308SHaikun.Wang@freescale.com 9855bc48308SHaikun.Wang@freescale.com static int fsl_qspi_ofdata_to_platdata(struct udevice *bus) 9865bc48308SHaikun.Wang@freescale.com { 9875bc48308SHaikun.Wang@freescale.com struct reg_data { 9885bc48308SHaikun.Wang@freescale.com u32 addr; 9895bc48308SHaikun.Wang@freescale.com u32 size; 9905bc48308SHaikun.Wang@freescale.com } regs_data[2]; 9915bc48308SHaikun.Wang@freescale.com struct fsl_qspi_platdata *plat = bus->platdata; 9925bc48308SHaikun.Wang@freescale.com const void *blob = gd->fdt_blob; 9935bc48308SHaikun.Wang@freescale.com int node = bus->of_offset; 9945bc48308SHaikun.Wang@freescale.com int ret, flash_num = 0, subnode; 9955bc48308SHaikun.Wang@freescale.com 9965bc48308SHaikun.Wang@freescale.com if (fdtdec_get_bool(blob, node, "big-endian")) 9975bc48308SHaikun.Wang@freescale.com plat->flags |= QSPI_FLAG_REGMAP_ENDIAN_BIG; 9985bc48308SHaikun.Wang@freescale.com 9995bc48308SHaikun.Wang@freescale.com ret = fdtdec_get_int_array(blob, node, "reg", (u32 *)regs_data, 10005bc48308SHaikun.Wang@freescale.com sizeof(regs_data)/sizeof(u32)); 10015bc48308SHaikun.Wang@freescale.com if (ret) { 10025bc48308SHaikun.Wang@freescale.com debug("Error: can't get base addresses (ret = %d)!\n", ret); 10035bc48308SHaikun.Wang@freescale.com return -ENOMEM; 10045bc48308SHaikun.Wang@freescale.com } 10055bc48308SHaikun.Wang@freescale.com 10065bc48308SHaikun.Wang@freescale.com /* Count flash numbers */ 10075bc48308SHaikun.Wang@freescale.com fdt_for_each_subnode(blob, subnode, node) 10085bc48308SHaikun.Wang@freescale.com ++flash_num; 10095bc48308SHaikun.Wang@freescale.com 10105bc48308SHaikun.Wang@freescale.com if (flash_num == 0) { 10115bc48308SHaikun.Wang@freescale.com debug("Error: Missing flashes!\n"); 10125bc48308SHaikun.Wang@freescale.com return -ENODEV; 10135bc48308SHaikun.Wang@freescale.com } 10145bc48308SHaikun.Wang@freescale.com 10155bc48308SHaikun.Wang@freescale.com plat->speed_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 10165bc48308SHaikun.Wang@freescale.com FSL_QSPI_DEFAULT_SCK_FREQ); 10175bc48308SHaikun.Wang@freescale.com plat->num_chipselect = fdtdec_get_int(blob, node, "num-cs", 10185bc48308SHaikun.Wang@freescale.com FSL_QSPI_MAX_CHIPSELECT_NUM); 10195bc48308SHaikun.Wang@freescale.com 10205bc48308SHaikun.Wang@freescale.com plat->reg_base = regs_data[0].addr; 10215bc48308SHaikun.Wang@freescale.com plat->amba_base = regs_data[1].addr; 10225bc48308SHaikun.Wang@freescale.com plat->amba_total_size = regs_data[1].size; 10235bc48308SHaikun.Wang@freescale.com plat->flash_num = flash_num; 10245bc48308SHaikun.Wang@freescale.com 10255bc48308SHaikun.Wang@freescale.com debug("%s: regs=<0x%x> <0x%x, 0x%x>, max-frequency=%d, endianess=%s\n", 10265bc48308SHaikun.Wang@freescale.com __func__, 10275bc48308SHaikun.Wang@freescale.com plat->reg_base, 10285bc48308SHaikun.Wang@freescale.com plat->amba_base, 10295bc48308SHaikun.Wang@freescale.com plat->amba_total_size, 10305bc48308SHaikun.Wang@freescale.com plat->speed_hz, 10315bc48308SHaikun.Wang@freescale.com plat->flags & QSPI_FLAG_REGMAP_ENDIAN_BIG ? "be" : "le" 10325bc48308SHaikun.Wang@freescale.com ); 10335bc48308SHaikun.Wang@freescale.com 10345bc48308SHaikun.Wang@freescale.com return 0; 10355bc48308SHaikun.Wang@freescale.com } 10365bc48308SHaikun.Wang@freescale.com 10375bc48308SHaikun.Wang@freescale.com static int fsl_qspi_xfer(struct udevice *dev, unsigned int bitlen, 10385bc48308SHaikun.Wang@freescale.com const void *dout, void *din, unsigned long flags) 10395bc48308SHaikun.Wang@freescale.com { 10405bc48308SHaikun.Wang@freescale.com struct fsl_qspi_priv *priv; 10415bc48308SHaikun.Wang@freescale.com struct udevice *bus; 10425bc48308SHaikun.Wang@freescale.com 10435bc48308SHaikun.Wang@freescale.com bus = dev->parent; 10445bc48308SHaikun.Wang@freescale.com priv = dev_get_priv(bus); 10455bc48308SHaikun.Wang@freescale.com 10465bc48308SHaikun.Wang@freescale.com return qspi_xfer(priv, bitlen, dout, din, flags); 10475bc48308SHaikun.Wang@freescale.com } 10485bc48308SHaikun.Wang@freescale.com 10495bc48308SHaikun.Wang@freescale.com static int fsl_qspi_claim_bus(struct udevice *dev) 10505bc48308SHaikun.Wang@freescale.com { 10515bc48308SHaikun.Wang@freescale.com struct fsl_qspi_priv *priv; 10525bc48308SHaikun.Wang@freescale.com struct udevice *bus; 10535bc48308SHaikun.Wang@freescale.com struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); 10545bc48308SHaikun.Wang@freescale.com 10555bc48308SHaikun.Wang@freescale.com bus = dev->parent; 10565bc48308SHaikun.Wang@freescale.com priv = dev_get_priv(bus); 10575bc48308SHaikun.Wang@freescale.com 10585bc48308SHaikun.Wang@freescale.com priv->cur_amba_base = 10595bc48308SHaikun.Wang@freescale.com priv->amba_base[0] + FSL_QSPI_FLASH_SIZE * slave_plat->cs; 10605bc48308SHaikun.Wang@freescale.com 10615bc48308SHaikun.Wang@freescale.com qspi_module_disable(priv, 0); 10625bc48308SHaikun.Wang@freescale.com 10635bc48308SHaikun.Wang@freescale.com return 0; 10645bc48308SHaikun.Wang@freescale.com } 10655bc48308SHaikun.Wang@freescale.com 10665bc48308SHaikun.Wang@freescale.com static int fsl_qspi_release_bus(struct udevice *dev) 10675bc48308SHaikun.Wang@freescale.com { 10685bc48308SHaikun.Wang@freescale.com struct fsl_qspi_priv *priv; 10695bc48308SHaikun.Wang@freescale.com struct udevice *bus; 10705bc48308SHaikun.Wang@freescale.com 10715bc48308SHaikun.Wang@freescale.com bus = dev->parent; 10725bc48308SHaikun.Wang@freescale.com priv = dev_get_priv(bus); 10735bc48308SHaikun.Wang@freescale.com 10745bc48308SHaikun.Wang@freescale.com qspi_module_disable(priv, 1); 10755bc48308SHaikun.Wang@freescale.com 10765bc48308SHaikun.Wang@freescale.com return 0; 10775bc48308SHaikun.Wang@freescale.com } 10785bc48308SHaikun.Wang@freescale.com 10795bc48308SHaikun.Wang@freescale.com static int fsl_qspi_set_speed(struct udevice *bus, uint speed) 10805bc48308SHaikun.Wang@freescale.com { 10815bc48308SHaikun.Wang@freescale.com /* Nothing to do */ 10825bc48308SHaikun.Wang@freescale.com return 0; 10835bc48308SHaikun.Wang@freescale.com } 10845bc48308SHaikun.Wang@freescale.com 10855bc48308SHaikun.Wang@freescale.com static int fsl_qspi_set_mode(struct udevice *bus, uint mode) 10865bc48308SHaikun.Wang@freescale.com { 10875bc48308SHaikun.Wang@freescale.com /* Nothing to do */ 10885bc48308SHaikun.Wang@freescale.com return 0; 10895bc48308SHaikun.Wang@freescale.com } 10905bc48308SHaikun.Wang@freescale.com 10915bc48308SHaikun.Wang@freescale.com static const struct dm_spi_ops fsl_qspi_ops = { 10925bc48308SHaikun.Wang@freescale.com .claim_bus = fsl_qspi_claim_bus, 10935bc48308SHaikun.Wang@freescale.com .release_bus = fsl_qspi_release_bus, 10945bc48308SHaikun.Wang@freescale.com .xfer = fsl_qspi_xfer, 10955bc48308SHaikun.Wang@freescale.com .set_speed = fsl_qspi_set_speed, 10965bc48308SHaikun.Wang@freescale.com .set_mode = fsl_qspi_set_mode, 10975bc48308SHaikun.Wang@freescale.com }; 10985bc48308SHaikun.Wang@freescale.com 10995bc48308SHaikun.Wang@freescale.com static const struct udevice_id fsl_qspi_ids[] = { 11005bc48308SHaikun.Wang@freescale.com { .compatible = "fsl,vf610-qspi" }, 11015bc48308SHaikun.Wang@freescale.com { .compatible = "fsl,imx6sx-qspi" }, 11025bc48308SHaikun.Wang@freescale.com { } 11035bc48308SHaikun.Wang@freescale.com }; 11045bc48308SHaikun.Wang@freescale.com 11055bc48308SHaikun.Wang@freescale.com U_BOOT_DRIVER(fsl_qspi) = { 11065bc48308SHaikun.Wang@freescale.com .name = "fsl_qspi", 11075bc48308SHaikun.Wang@freescale.com .id = UCLASS_SPI, 11085bc48308SHaikun.Wang@freescale.com .of_match = fsl_qspi_ids, 11095bc48308SHaikun.Wang@freescale.com .ops = &fsl_qspi_ops, 11105bc48308SHaikun.Wang@freescale.com .ofdata_to_platdata = fsl_qspi_ofdata_to_platdata, 11115bc48308SHaikun.Wang@freescale.com .platdata_auto_alloc_size = sizeof(struct fsl_qspi_platdata), 11125bc48308SHaikun.Wang@freescale.com .priv_auto_alloc_size = sizeof(struct fsl_qspi_priv), 11135bc48308SHaikun.Wang@freescale.com .probe = fsl_qspi_probe, 11145bc48308SHaikun.Wang@freescale.com .child_pre_probe = fsl_qspi_child_pre_probe, 11155bc48308SHaikun.Wang@freescale.com }; 11165bc48308SHaikun.Wang@freescale.com #endif 1117