diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/arasan_nfc.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/zynq_nand.c | 1186 | ||||
-rw-r--r-- | drivers/mtd/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/spi/sandbox.c | 11 | ||||
-rw-r--r-- | drivers/mtd/spi/sf.c | 4 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_dataflash.c | 178 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_internal.h | 72 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_params.c | 149 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash.c | 236 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash_ids.c | 184 | ||||
-rw-r--r-- | drivers/mtd/spi/sunxi_spi_spl.c | 3 |
14 files changed, 1584 insertions, 454 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index df154bf..65bb040 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -87,6 +87,13 @@ config NAND_MXS This enables NAND driver for the NAND flash controller on the MXS processors. +config NAND_ZYNQ + bool "Support for Zynq Nand controller" + select SYS_NAND_SELF_INIT + help + This enables Nand driver support for Nand flash controller + found on Zynq SoC. + comment "Generic NAND options" # Enhance depends when converting drivers to Kconfig which use this config diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1df9273..fd4bb66 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o obj-$(CONFIG_NAND_PLAT) += nand_plat.o obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o +obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o else # minimal SPL drivers diff --git a/drivers/mtd/nand/arasan_nfc.c b/drivers/mtd/nand/arasan_nfc.c index 86f7526..a8f795d 100644 --- a/drivers/mtd/nand/arasan_nfc.c +++ b/drivers/mtd/nand/arasan_nfc.c @@ -853,6 +853,8 @@ static int arasan_nand_send_rdcmd(struct arasan_nand_command_format *curr_cmd, reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT); } + reg_val &= ~ARASAN_NAND_CMD_ECC_ON_MASK; + reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; addr_cycles = arasan_nand_get_addrcycle(mtd); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index ce0a14e..d36f900 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -46,6 +46,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"TC58NVG2S0F 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG2S0H 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, {"TC58NVG3S0F 8G 3.3V 8-bit", { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, diff --git a/drivers/mtd/nand/zynq_nand.c b/drivers/mtd/nand/zynq_nand.c new file mode 100644 index 0000000..cb3340d --- /dev/null +++ b/drivers/mtd/nand/zynq_nand.c @@ -0,0 +1,1186 @@ +/* + * (C) Copyright 2016 Xilinx, Inc. + * + * Xilinx Zynq NAND Flash Controller Driver + * This driver is based on plat_nand.c and mxc_nand.c drivers + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <nand.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/arch/hardware.h> + +/* The NAND flash driver defines */ +#define ZYNQ_NAND_CMD_PHASE 1 +#define ZYNQ_NAND_DATA_PHASE 2 +#define ZYNQ_NAND_ECC_SIZE 512 +#define ZYNQ_NAND_SET_OPMODE_8BIT (0 << 0) +#define ZYNQ_NAND_SET_OPMODE_16BIT (1 << 0) +#define ZYNQ_NAND_ECC_STATUS (1 << 6) +#define ZYNQ_MEMC_CLRCR_INT_CLR1 (1 << 4) +#define ZYNQ_MEMC_SR_RAW_INT_ST1 (1 << 6) +#define ZYNQ_MEMC_SR_INT_ST1 (1 << 4) +#define ZYNQ_MEMC_NAND_ECC_MODE_MASK 0xC + +/* Flash memory controller operating parameters */ +#define ZYNQ_NAND_CLR_CONFIG ((0x1 << 1) | /* Disable interrupt */ \ + (0x1 << 4) | /* Clear interrupt */ \ + (0x1 << 6)) /* Disable ECC interrupt */ + +/* Assuming 50MHz clock (20ns cycle time) and 3V operation */ +#define ZYNQ_NAND_SET_CYCLES ((0x2 << 20) | /* t_rr from nand_cycles */ \ + (0x2 << 17) | /* t_ar from nand_cycles */ \ + (0x1 << 14) | /* t_clr from nand_cycles */ \ + (0x3 << 11) | /* t_wp from nand_cycles */ \ + (0x2 << 8) | /* t_rea from nand_cycles */ \ + (0x5 << 4) | /* t_wc from nand_cycles */ \ + (0x5 << 0)) /* t_rc from nand_cycles */ + + +#define ZYNQ_NAND_DIRECT_CMD ((0x4 << 23) | /* Chip 0 from interface 1 */ \ + (0x2 << 21)) /* UpdateRegs operation */ + +#define ZYNQ_NAND_ECC_CONFIG ((0x1 << 2) | /* ECC available on APB */ \ + (0x1 << 4) | /* ECC read at end of page */ \ + (0x0 << 5)) /* No Jumping */ + +#define ZYNQ_NAND_ECC_CMD1 ((0x80) | /* Write command */ \ + (0x00 << 8) | /* Read command */ \ + (0x30 << 16) | /* Read End command */ \ + (0x1 << 24)) /* Read End command calid */ + +#define ZYNQ_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \ + (0x05 << 8) | /* Read col change cmd */ \ + (0xE0 << 16) | /* Read col change end cmd */ \ + (0x1 << 24)) /* Read col change + end cmd valid */ +/* AXI Address definitions */ +#define START_CMD_SHIFT 3 +#define END_CMD_SHIFT 11 +#define END_CMD_VALID_SHIFT 20 +#define ADDR_CYCLES_SHIFT 21 +#define CLEAR_CS_SHIFT 21 +#define ECC_LAST_SHIFT 10 +#define COMMAND_PHASE (0 << 19) +#define DATA_PHASE (1 << 19) +#define ONDIE_ECC_FEATURE_ADDR 0x90 +#define ONDIE_ECC_FEATURE_ENABLE 0x08 + +#define ZYNQ_NAND_ECC_LAST (1 << ECC_LAST_SHIFT) /* Set ECC_Last */ +#define ZYNQ_NAND_CLEAR_CS (1 << CLEAR_CS_SHIFT) /* Clear chip select */ + +/* ECC block registers bit position and bit mask */ +#define ZYNQ_NAND_ECC_BUSY (1 << 6) /* ECC block is busy */ +#define ZYNQ_NAND_ECC_MASK 0x00FFFFFF /* ECC value mask */ + + +/* SMC register set */ +struct zynq_nand_smc_regs { + u32 csr; /* 0x00 */ + u32 reserved0[2]; + u32 cfr; /* 0x0C */ + u32 dcr; /* 0x10 */ + u32 scr; /* 0x14 */ + u32 sor; /* 0x18 */ + u32 reserved1[249]; + u32 esr; /* 0x400 */ + u32 emcr; /* 0x404 */ + u32 emcmd1r; /* 0x408 */ + u32 emcmd2r; /* 0x40C */ + u32 reserved2[2]; + u32 eval0r; /* 0x418 */ +}; +#define zynq_nand_smc_base ((struct zynq_nand_smc_regs __iomem *)\ + ZYNQ_SMC_BASEADDR) + +/* + * struct zynq_nand_info - Defines the NAND flash driver instance + * @parts: Pointer to the mtd_partition structure + * @nand_base: Virtual address of the NAND flash device + * @end_cmd_pending: End command is pending + * @end_cmd: End command + */ +struct zynq_nand_info { + void __iomem *nand_base; + u8 end_cmd_pending; + u8 end_cmd; +}; + +/* + * struct zynq_nand_command_format - Defines NAND flash command format + * @start_cmd: First cycle command (Start command) + * @end_cmd: Second cycle command (Last command) + * @addr_cycles: Number of address cycles required to send the address + * @end_cmd_valid: The second cycle command is valid for cmd or data phase + */ +struct zynq_nand_command_format { + u8 start_cmd; + u8 end_cmd; + u8 addr_cycles; + u8 end_cmd_valid; +}; + +/* The NAND flash operations command format */ +static const struct zynq_nand_command_format zynq_nand_commands[] = { + {NAND_CMD_READ0, NAND_CMD_READSTART, 5, ZYNQ_NAND_CMD_PHASE}, + {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, ZYNQ_NAND_CMD_PHASE}, + {NAND_CMD_READID, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_STATUS, NAND_CMD_NONE, 0, 0}, + {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, ZYNQ_NAND_DATA_PHASE}, + {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, 0}, + {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, ZYNQ_NAND_CMD_PHASE}, + {NAND_CMD_RESET, NAND_CMD_NONE, 0, 0}, + {NAND_CMD_PARAM, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0}, + /* Add all the flash commands supported by the flash device */ +}; + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + { .offset = 8, .length = 8 } + } +}; + +static struct nand_ecclayout nand_oob_64 = { + .eccbytes = 12, + .eccpos = { + 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63}, + .oobfree = { + { .offset = 2, .length = 50 } + } +}; + +static struct nand_ecclayout ondie_nand_oob_64 = { + .eccbytes = 32, + + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 24, 25, 26, 27, 28, 29, 30, 31, + 40, 41, 42, 43, 44, 45, 46, 47, + 56, 57, 58, 59, 60, 61, 62, 63 + }, + + .oobfree = { + { .offset = 4, .length = 4 }, + { .offset = 20, .length = 4 }, + { .offset = 36, .length = 4 }, + { .offset = 52, .length = 4 } + } +}; + +/* bbt decriptors for chips with on-die ECC and + chips with 64-byte OOB */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/* + * zynq_nand_waitfor_ecc_completion - Wait for ECC completion + * + * returns: status for command completion, -1 for Timeout + */ +static int zynq_nand_waitfor_ecc_completion(void) +{ + unsigned long timeout; + u32 status; + + /* Wait max 10us */ + timeout = 10; + status = readl(&zynq_nand_smc_base->esr); + while (status & ZYNQ_NAND_ECC_BUSY) { + status = readl(&zynq_nand_smc_base->esr); + if (timeout == 0) + return -1; + timeout--; + udelay(1); + } + + return status; +} + +/* + * zynq_nand_init_nand_flash - Initialize NAND controller + * @option: Device property flags + * + * This function initializes the NAND flash interface on the NAND controller. + * + * returns: 0 on success or error value on failure + */ +static int zynq_nand_init_nand_flash(int option) +{ + u32 status; + + /* disable interrupts */ + writel(ZYNQ_NAND_CLR_CONFIG, &zynq_nand_smc_base->cfr); + /* Initialize the NAND interface by setting cycles and operation mode */ + writel(ZYNQ_NAND_SET_CYCLES, &zynq_nand_smc_base->scr); + if (option & NAND_BUSWIDTH_16) + writel(ZYNQ_NAND_SET_OPMODE_16BIT, &zynq_nand_smc_base->sor); + else + writel(ZYNQ_NAND_SET_OPMODE_8BIT, &zynq_nand_smc_base->sor); + + writel(ZYNQ_NAND_DIRECT_CMD, &zynq_nand_smc_base->dcr); + + /* Wait till the ECC operation is complete */ + status = zynq_nand_waitfor_ecc_completion(); + if (status < 0) { + printf("%s: Timeout\n", __func__); + return status; + } + + /* Set the command1 and command2 register */ + writel(ZYNQ_NAND_ECC_CMD1, &zynq_nand_smc_base->emcmd1r); + writel(ZYNQ_NAND_ECC_CMD2, &zynq_nand_smc_base->emcmd2r); + + return 0; +} + +/* + * zynq_nand_calculate_hwecc - Calculate Hardware ECC + * @mtd: Pointer to the mtd_info structure + * @data: Pointer to the page data + * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored + * + * This function retrieves the Hardware ECC data from the controller and returns + * ECC data back to the MTD subsystem. + * + * returns: 0 on success or error value on failure + */ +static int zynq_nand_calculate_hwecc(struct mtd_info *mtd, const u8 *data, + u8 *ecc_code) +{ + u32 ecc_value = 0; + u8 ecc_reg, ecc_byte; + u32 ecc_status; + + /* Wait till the ECC operation is complete */ + ecc_status = zynq_nand_waitfor_ecc_completion(); + if (ecc_status < 0) { + printf("%s: Timeout\n", __func__); + return ecc_status; + } + + for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) { + /* Read ECC value for each block */ + ecc_value = readl(&zynq_nand_smc_base->eval0r + ecc_reg); + + /* Get the ecc status from ecc read value */ + ecc_status = (ecc_value >> 24) & 0xFF; + + /* ECC value valid */ + if (ecc_status & ZYNQ_NAND_ECC_STATUS) { + for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) { + /* Copy ECC bytes to MTD buffer */ + *ecc_code = ecc_value & 0xFF; + ecc_value = ecc_value >> 8; + ecc_code++; + } + } else { + debug("%s: ecc status failed\n", __func__); + } + } + + return 0; +} + +/* + * onehot - onehot function + * @value: value to check for onehot + * + * This function checks whether a value is onehot or not. + * onehot is if and only if one bit is set. + * + * FIXME: Try to move this in common.h + */ +static bool onehot(unsigned short value) +{ + bool onehot; + + onehot = value && !(value & (value - 1)); + return onehot; +} + +/* + * zynq_nand_correct_data - ECC correction function + * @mtd: Pointer to the mtd_info structure + * @buf: Pointer to the page data + * @read_ecc: Pointer to the ECC value read from spare data area + * @calc_ecc: Pointer to the calculated ECC value + * + * This function corrects the ECC single bit errors & detects 2-bit errors. + * + * returns: 0 if no ECC errors found + * 1 if single bit error found and corrected. + * -1 if multiple ECC errors found. + */ +static int zynq_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + unsigned char bit_addr; + unsigned int byte_addr; + unsigned short ecc_odd, ecc_even; + unsigned short read_ecc_lower, read_ecc_upper; + unsigned short calc_ecc_lower, calc_ecc_upper; + + read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff; + read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff; + + calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff; + calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff; + + ecc_odd = read_ecc_lower ^ calc_ecc_lower; + ecc_even = read_ecc_upper ^ calc_ecc_upper; + + if ((ecc_odd == 0) && (ecc_even == 0)) + return 0; /* no error */ + + if (ecc_odd == (~ecc_even & 0xfff)) { + /* bits [11:3] of error code is byte offset */ + byte_addr = (ecc_odd >> 3) & 0x1ff; + /* bits [2:0] of error code is bit offset */ + bit_addr = ecc_odd & 0x7; + /* Toggling error bit */ + buf[byte_addr] ^= (1 << bit_addr); + return 1; + } + + if (onehot(ecc_odd | ecc_even)) + return 1; /* one error in parity */ + + return -1; /* Uncorrectable error */ +} + +/* + * zynq_nand_read_oob - [REPLACABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int zynq_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + unsigned long data_phase_addr = 0; + int data_width = 4; + u8 *p; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + + p = chip->oob_poi; + chip->read_buf(mtd, p, (mtd->oobsize - data_width)); + p += mtd->oobsize - data_width; + + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + chip->read_buf(mtd, p, data_width); + + return 0; +} + +/* + * zynq_nand_write_oob - [REPLACABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int zynq_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0, data_width = 4; + const u8 *buf = chip->oob_poi; + unsigned long data_phase_addr = 0; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + + chip->write_buf(mtd, buf, (mtd->oobsize - data_width)); + buf += mtd->oobsize - data_width; + + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + chip->write_buf(mtd, buf, data_width); + + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* + * zynq_nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to read + */ +static int zynq_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + unsigned long data_width = 4; + unsigned long data_phase_addr = 0; + u8 *p; + + chip->read_buf(mtd, buf, mtd->writesize); + + p = chip->oob_poi; + chip->read_buf(mtd, p, (mtd->oobsize - data_width)); + p += (mtd->oobsize - data_width); + + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + + chip->read_buf(mtd, p, data_width); + return 0; +} + +static int zynq_nand_read_page_raw_nooob(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + chip->read_buf(mtd, buf, mtd->writesize); + return 0; +} + +static int zynq_nand_read_subpage_raw(struct mtd_info *mtd, + struct nand_chip *chip, u32 data_offs, + u32 readlen, u8 *buf, int page) +{ + if (data_offs != 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1); + buf += data_offs; + } + chip->read_buf(mtd, buf, readlen); + + return 0; +} + +/* + * zynq_nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int zynq_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, int page) +{ + unsigned long data_width = 4; + unsigned long data_phase_addr = 0; + u8 *p; + + chip->write_buf(mtd, buf, mtd->writesize); + + p = chip->oob_poi; + chip->write_buf(mtd, p, (mtd->oobsize - data_width)); + p += (mtd->oobsize - data_width); + + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + + chip->write_buf(mtd, p, data_width); + + return 0; +} + +/* + * nand_write_page_hwecc - Hardware ECC based page write function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * This functions writes data and hardware generated ECC values in to the page. + */ +static int zynq_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, int page) +{ + int i, eccsteps, eccsize = chip->ecc.size; + u8 *ecc_calc = chip->buffers->ecccalc; + const u8 *p = buf; + u32 *eccpos = chip->ecc.layout->eccpos; + unsigned long data_phase_addr = 0; + unsigned long data_width = 4; + u8 *oob_ptr; + + for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) { + chip->write_buf(mtd, p, eccsize); + p += eccsize; + } + chip->write_buf(mtd, p, (eccsize - data_width)); + p += eccsize - data_width; + + /* Set ECC Last bit to 1 */ + data_phase_addr = (unsigned long) chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + chip->write_buf(mtd, p, data_width); + + /* Wait for ECC to be calculated and read the error values */ + p = buf; + chip->ecc.calculate(mtd, p, &ecc_calc[0]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]); + + /* Clear ECC last bit */ + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr &= ~ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + + /* Write the spare area with ECC bytes */ + oob_ptr = chip->oob_poi; + chip->write_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); + + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + oob_ptr += (mtd->oobsize - data_width); + chip->write_buf(mtd, oob_ptr, data_width); + + return 0; +} + +/* + * zynq_nand_write_page_swecc - [REPLACABLE] software ecc based page + * write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int zynq_nand_write_page_swecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + u8 *ecc_calc = chip->buffers->ecccalc; + const u8 *p = buf; + u32 *eccpos = chip->ecc.layout->eccpos; + + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); +} + +/* + * nand_read_page_hwecc - Hardware ECC based page read function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the buffer to store read data + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to read + * + * This functions reads data and checks the data integrity by comparing hardware + * generated ECC values and read ECC values from spare area. + * + * returns: 0 always and updates ECC operation status in to MTD structure + */ +static int zynq_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + int i, stat, eccsteps, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + u8 *p = buf; + u8 *ecc_calc = chip->buffers->ecccalc; + u8 *ecc_code = chip->buffers->ecccode; + u32 *eccpos = chip->ecc.layout->eccpos; + unsigned long data_phase_addr = 0; + unsigned long data_width = 4; + u8 *oob_ptr; + + for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) { + chip->read_buf(mtd, p, eccsize); + p += eccsize; + } + chip->read_buf(mtd, p, (eccsize - data_width)); + p += eccsize - data_width; + + /* Set ECC Last bit to 1 */ + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + chip->read_buf(mtd, p, data_width); + + /* Read the calculated ECC value */ + p = buf; + chip->ecc.calculate(mtd, p, &ecc_calc[0]); + + /* Clear ECC last bit */ + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr &= ~ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + + /* Read the stored ECC value */ + oob_ptr = chip->oob_poi; + chip->read_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); + + /* de-assert chip select */ + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + + oob_ptr += (mtd->oobsize - data_width); + chip->read_buf(mtd, oob_ptr, data_width); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = ~(chip->oob_poi[eccpos[i]]); + + eccsteps = chip->ecc.steps; + p = buf; + + /* Check ECC error for all blocks and correct if it is correctable */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/* + * zynq_nand_read_page_swecc - [REPLACABLE] software ecc based page + * read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + */ +static int zynq_nand_read_page_swecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + u8 *p = buf; + u8 *ecc_calc = chip->buffers->ecccalc; + u8 *ecc_code = chip->buffers->ecccode; + u32 *eccpos = chip->ecc.layout->eccpos; + + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/* + * zynq_nand_select_chip - Select the flash device + * @mtd: Pointer to the mtd_info structure + * @chip: Chip number to be selected + * + * This function is empty as the NAND controller handles chip select line + * internally based on the chip address passed in command and data phase. + */ +static void zynq_nand_select_chip(struct mtd_info *mtd, int chip) +{ + /* Not support multiple chips yet */ +} + +/* + * zynq_nand_cmd_function - Send command to NAND device + * @mtd: Pointer to the mtd_info structure + * @command: The command to be sent to the flash device + * @column: The column address for this command, -1 if none + * @page_addr: The page address for this command, -1 if none + */ +static void zynq_nand_cmd_function(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + const struct zynq_nand_command_format *curr_cmd = NULL; + struct zynq_nand_info *xnand = (struct zynq_nand_info *)chip->priv; + void *cmd_addr; + unsigned long cmd_data = 0; + unsigned long cmd_phase_addr = 0; + unsigned long data_phase_addr = 0; + u8 end_cmd = 0; + u8 end_cmd_valid = 0; + u32 index; + + if (xnand->end_cmd_pending) { + /* Check for end command if this command request is same as the + * pending command then return + */ + if (xnand->end_cmd == command) { + xnand->end_cmd = 0; + xnand->end_cmd_pending = 0; + return; + } + } + + /* Emulate NAND_CMD_READOOB for large page device */ + if ((mtd->writesize > ZYNQ_NAND_ECC_SIZE) && + (command == NAND_CMD_READOOB)) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Get the command format */ + for (index = 0; index < ARRAY_SIZE(zynq_nand_commands); index++) + if (command == zynq_nand_commands[index].start_cmd) + break; + + if (index == ARRAY_SIZE(zynq_nand_commands)) { + printf("%s: Unsupported start cmd %02x\n", __func__, command); + return; + } + curr_cmd = &zynq_nand_commands[index]; + + /* Clear interrupt */ + writel(ZYNQ_MEMC_CLRCR_INT_CLR1, &zynq_nand_smc_base->cfr); + + /* Get the command phase address */ + if (curr_cmd->end_cmd_valid == ZYNQ_NAND_CMD_PHASE) + end_cmd_valid = 1; + + if (curr_cmd->end_cmd == NAND_CMD_NONE) + end_cmd = 0x0; + else + end_cmd = curr_cmd->end_cmd; + + cmd_phase_addr = (unsigned long)xnand->nand_base | + (curr_cmd->addr_cycles << ADDR_CYCLES_SHIFT) | + (end_cmd_valid << END_CMD_VALID_SHIFT) | + (COMMAND_PHASE) | + (end_cmd << END_CMD_SHIFT) | + (curr_cmd->start_cmd << START_CMD_SHIFT); + + cmd_addr = (void __iomem *)cmd_phase_addr; + + /* Get the data phase address */ + end_cmd_valid = 0; + + data_phase_addr = (unsigned long)xnand->nand_base | + (0x0 << CLEAR_CS_SHIFT) | + (end_cmd_valid << END_CMD_VALID_SHIFT) | + (DATA_PHASE) | + (end_cmd << END_CMD_SHIFT) | + (0x0 << ECC_LAST_SHIFT); + + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + chip->IO_ADDR_W = chip->IO_ADDR_R; + + /* Command phase AXI Read & Write */ + if (column != -1 && page_addr != -1) { + /* Adjust columns for 16 bit bus width */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + cmd_data = column; + if (mtd->writesize > ZYNQ_NAND_ECC_SIZE) { + cmd_data |= page_addr << 16; + /* Another address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) { + writel(cmd_data, cmd_addr); + cmd_data = (page_addr >> 16); + } + } else { + cmd_data |= page_addr << 8; + } + } else if (page_addr != -1) { /* Erase */ + cmd_data = page_addr; + } else if (column != -1) { /* Change read/write column, read id etc */ + /* Adjust columns for 16 bit bus width */ + if ((chip->options & NAND_BUSWIDTH_16) && + ((command == NAND_CMD_READ0) || + (command == NAND_CMD_SEQIN) || + (command == NAND_CMD_RNDOUT) || + (command == NAND_CMD_RNDIN))) + column >>= 1; + cmd_data = column; + } + + writel(cmd_data, cmd_addr); + + if (curr_cmd->end_cmd_valid) { + xnand->end_cmd = curr_cmd->end_cmd; + xnand->end_cmd_pending = 1; + } + + ndelay(100); + + if ((command == NAND_CMD_READ0) || + (command == NAND_CMD_RESET) || + (command == NAND_CMD_PARAM) || + (command == NAND_CMD_GET_FEATURES)) + /* wait until command is processed */ + nand_wait_ready(mtd); +} + +/* + * zynq_nand_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void zynq_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + /* Make sure that buf is 32 bit aligned */ + if (((unsigned long)buf & 0x3) != 0) { + if (((unsigned long)buf & 0x1) != 0) { + if (len) { + *buf = readb(chip->IO_ADDR_R); + buf += 1; + len--; + } + } + + if (((unsigned long)buf & 0x3) != 0) { + if (len >= 2) { + *(u16 *)buf = readw(chip->IO_ADDR_R); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + *(u32 *)buf = readl(chip->IO_ADDR_R); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + *(u16 *)buf = readw(chip->IO_ADDR_R); + buf += 2; + len -= 2; + } + if (len) + *buf = readb(chip->IO_ADDR_R); + } +} + +/* + * zynq_nand_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void zynq_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + const u32 *nand = chip->IO_ADDR_W; + + /* Make sure that buf is 32 bit aligned */ + if (((unsigned long)buf & 0x3) != 0) { + if (((unsigned long)buf & 0x1) != 0) { + if (len) { + writeb(*buf, nand); + buf += 1; + len--; + } + } + + if (((unsigned long)buf & 0x3) != 0) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + writel(*(u32 *)buf, nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + + if (len) + writeb(*buf, nand); + } +} + +/* + * zynq_nand_device_ready - Check device ready/busy line + * @mtd: Pointer to the mtd_info structure + * + * returns: 0 on busy or 1 on ready state + */ +static int zynq_nand_device_ready(struct mtd_info *mtd) +{ + u32 csr_val; + + csr_val = readl(&zynq_nand_smc_base->csr); + /* Check the raw_int_status1 bit */ + if (csr_val & ZYNQ_MEMC_SR_RAW_INT_ST1) { + /* Clear the interrupt condition */ + writel(ZYNQ_MEMC_SR_INT_ST1, &zynq_nand_smc_base->cfr); + return 1; + } + + return 0; +} + +static int zynq_nand_init(struct nand_chip *nand_chip, int devnum) +{ + struct zynq_nand_info *xnand; + struct mtd_info *mtd; + unsigned long ecc_page_size; + u8 maf_id, dev_id, i; + u8 get_feature[4]; + u8 set_feature[4] = {ONDIE_ECC_FEATURE_ENABLE, 0x00, 0x00, 0x00}; + unsigned long ecc_cfg; + int ondie_ecc_enabled = 0; + int err = -1; + + xnand = calloc(1, sizeof(struct zynq_nand_info)); + if (!xnand) { + printf("%s: failed to allocate\n", __func__); + goto fail; + } + + xnand->nand_base = (void __iomem *)ZYNQ_NAND_BASEADDR; + mtd = (struct mtd_info *)&nand_info[0]; + + nand_chip->priv = xnand; + mtd->priv = nand_chip; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = xnand->nand_base; + nand_chip->IO_ADDR_W = xnand->nand_base; + + /* Set the driver entry points for MTD */ + nand_chip->cmdfunc = zynq_nand_cmd_function; + nand_chip->dev_ready = zynq_nand_device_ready; + nand_chip->select_chip = zynq_nand_select_chip; + + /* If we don't set this delay driver sets 20us by default */ + nand_chip->chip_delay = 30; + + /* Buffer read/write routines */ + nand_chip->read_buf = zynq_nand_read_buf; + nand_chip->write_buf = zynq_nand_write_buf; + + nand_chip->bbt_options = NAND_BBT_USE_FLASH; + + /* Initialize the NAND flash interface on NAND controller */ + if (zynq_nand_init_nand_flash(nand_chip->options) < 0) { + printf("%s: nand flash init failed\n", __func__); + goto fail; + } + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1, NULL)) { + printf("%s: nand_scan_ident failed\n", __func__); + goto fail; + } + /* Send the command for reading device ID */ + nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + maf_id = nand_chip->read_byte(mtd); + dev_id = nand_chip->read_byte(mtd); + + if ((maf_id == 0x2c) && ((dev_id == 0xf1) || + (dev_id == 0xa1) || (dev_id == 0xb1) || + (dev_id == 0xaa) || (dev_id == 0xba) || + (dev_id == 0xda) || (dev_id == 0xca) || + (dev_id == 0xac) || (dev_id == 0xbc) || + (dev_id == 0xdc) || (dev_id == 0xcc) || + (dev_id == 0xa3) || (dev_id == 0xb3) || + (dev_id == 0xd3) || (dev_id == 0xc3))) { + nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + for (i = 0; i < 4; i++) + writeb(set_feature[i], nand_chip->IO_ADDR_W); + + /* Wait for 1us after writing data with SET_FEATURES command */ + ndelay(1000); + + nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + nand_chip->read_buf(mtd, get_feature, 4); + + if (get_feature[0] & ONDIE_ECC_FEATURE_ENABLE) { + debug("%s: OnDie ECC flash\n", __func__); + ondie_ecc_enabled = 1; + } else { + printf("%s: Unable to detect OnDie ECC\n", __func__); + } + } + + if (ondie_ecc_enabled) { + /* Bypass the controller ECC block */ + ecc_cfg = readl(&zynq_nand_smc_base->emcr); + ecc_cfg &= ~ZYNQ_MEMC_NAND_ECC_MODE_MASK; + writel(ecc_cfg, &zynq_nand_smc_base->emcr); + + /* The software ECC routines won't work + * with the SMC controller + */ + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.strength = 1; + nand_chip->ecc.read_page = zynq_nand_read_page_raw_nooob; + nand_chip->ecc.read_subpage = zynq_nand_read_subpage_raw; + nand_chip->ecc.write_page = zynq_nand_write_page_raw; + nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw; + nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw; + nand_chip->ecc.read_oob = zynq_nand_read_oob; + nand_chip->ecc.write_oob = zynq_nand_write_oob; + nand_chip->ecc.size = mtd->writesize; + nand_chip->ecc.bytes = 0; + + /* NAND with on-die ECC supports subpage reads */ + nand_chip->options |= NAND_SUBPAGE_READ; + + /* On-Die ECC spare bytes offset 8 is used for ECC codes */ + if (ondie_ecc_enabled) { + nand_chip->ecc.layout = &ondie_nand_oob_64; + /* Use the BBT pattern descriptors */ + nand_chip->bbt_td = &bbt_main_descr; + nand_chip->bbt_md = &bbt_mirror_descr; + } + } else { + /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */ + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.strength = 1; + nand_chip->ecc.size = ZYNQ_NAND_ECC_SIZE; + nand_chip->ecc.bytes = 3; + nand_chip->ecc.calculate = zynq_nand_calculate_hwecc; + nand_chip->ecc.correct = zynq_nand_correct_data; + nand_chip->ecc.hwctl = NULL; + nand_chip->ecc.read_page = zynq_nand_read_page_hwecc; + nand_chip->ecc.write_page = zynq_nand_write_page_hwecc; + nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw; + nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw; + nand_chip->ecc.read_oob = zynq_nand_read_oob; + nand_chip->ecc.write_oob = zynq_nand_write_oob; + + switch (mtd->writesize) { + case 512: + ecc_page_size = 0x1; + /* Set the ECC memory config register */ + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), + &zynq_nand_smc_base->emcr); + break; + case 1024: + ecc_page_size = 0x2; + /* Set the ECC memory config register */ + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), + &zynq_nand_smc_base->emcr); + break; + case 2048: + ecc_page_size = 0x3; + /* Set the ECC memory config register */ + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), + &zynq_nand_smc_base->emcr); + break; + default: + nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.calculate = nand_calculate_ecc; + nand_chip->ecc.correct = nand_correct_data; + nand_chip->ecc.read_page = zynq_nand_read_page_swecc; + nand_chip->ecc.write_page = zynq_nand_write_page_swecc; + nand_chip->ecc.size = 256; + break; + } + + if (mtd->oobsize == 16) + nand_chip->ecc.layout = &nand_oob_16; + else if (mtd->oobsize == 64) + nand_chip->ecc.layout = &nand_oob_64; + else + printf("%s: No oob layout found\n", __func__); + } + + /* Second phase scan */ + if (nand_scan_tail(mtd)) { + printf("%s: nand_scan_tail failed\n", __func__); + goto fail; + } + if (nand_register(devnum, mtd)) + goto fail; + return 0; +fail: + free(xnand); + return err; +} + +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; + +void board_nand_init(void) +{ + struct nand_chip *nand = &nand_chip[0]; + + if (zynq_nand_init(nand, 0)) + puts("ZYNQ NAND init failed\n"); +} diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index f3dc409..fcda023 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o obj-$(CONFIG_SPL_SPI_SUNXI) += sunxi_spi_spl.o endif -obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o sf_params.o sf.o +obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o spi_flash_ids.o sf.o obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index f59134f..4944059 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -88,7 +88,7 @@ struct sandbox_spi_flash { /* The current flash status (see STAT_XXX defines above) */ u16 status; /* Data describing the flash we're emulating */ - const struct spi_flash_params *data; + const struct spi_flash_info *data; /* The file on disk to serv up data from */ int fd; }; @@ -112,7 +112,7 @@ static int sandbox_sf_probe(struct udevice *dev) struct sandbox_spi_flash *sbsf = dev_get_priv(dev); const char *file; size_t len, idname_len; - const struct spi_flash_params *data; + const struct spi_flash_info *data; struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev); struct sandbox_state *state = state_get_current(); struct udevice *bus = dev->parent; @@ -168,7 +168,7 @@ static int sandbox_sf_probe(struct udevice *dev) } debug("%s: device='%s'\n", __func__, spec); - for (data = spi_flash_params_table; data->name; data++) { + for (data = spi_flash_ids; data->name; data++) { len = strlen(data->name); if (idname_len != len) continue; @@ -289,7 +289,7 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, /* we only support erase here */ if (sbsf->cmd == CMD_ERASE_CHIP) { sbsf->erase_size = sbsf->data->sector_size * - sbsf->data->nr_sectors; + sbsf->data->n_sectors; } else if (sbsf->cmd == CMD_ERASE_4K && (flags & SECT_4K)) { sbsf->erase_size = 4 << 10; } else if (sbsf->cmd == CMD_ERASE_64K && !(flags & SECT_4K)) { @@ -359,7 +359,8 @@ static int sandbox_sf_xfer(struct udevice *dev, unsigned int bitlen, debug(" id: off:%u tx:", sbsf->off); if (sbsf->off < IDCODE_LEN) { /* Extract correct byte from ID 0x00aabbcc */ - id = sbsf->data->jedec >> + id = ((JEDEC_MFR(sbsf->data) << 16) | + JEDEC_ID(sbsf->data)) >> (8 * (IDCODE_LEN - 1 - sbsf->off)); } else { id = 0; diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c index 664e860..d5e175c 100644 --- a/drivers/mtd/spi/sf.c +++ b/drivers/mtd/spi/sf.c @@ -18,10 +18,6 @@ static int spi_flash_read_write(struct spi_slave *spi, unsigned long flags = SPI_XFER_BEGIN; int ret; -#ifdef CONFIG_SF_DUAL_FLASH - if (spi->flags & SPI_XFER_U_PAGE) - flags |= SPI_XFER_U_PAGE; -#endif if (data_len == 0) flags |= SPI_XFER_END; diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c index b2a56da..bcddfa0 100644 --- a/drivers/mtd/spi/sf_dataflash.c +++ b/drivers/mtd/spi/sf_dataflash.c @@ -1,12 +1,12 @@ /* - * * Atmel DataFlash probing * * Copyright (C) 2004-2009, 2015 Freescale Semiconductor, Inc. * Haikun Wang (haikun.wang@freescale.com) * * SPDX-License-Identifier: GPL-2.0+ -*/ + */ + #include <common.h> #include <dm.h> #include <errno.h> @@ -67,15 +67,12 @@ #define OP_WRITE_SECURITY_REVC 0x9A #define OP_WRITE_SECURITY 0x9B /* revision D */ - struct dataflash { uint8_t command[16]; unsigned short page_offset; /* offset in flash address */ }; -/* - * Return the status of the DataFlash device. - */ +/* Return the status of the DataFlash device */ static inline int dataflash_status(struct spi_slave *spi) { int ret; @@ -114,9 +111,7 @@ static int dataflash_waitready(struct spi_slave *spi) return -ETIME; } -/* - * Erase pages of flash. - */ +/* Erase pages of flash */ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len) { struct dataflash *dataflash; @@ -147,7 +142,7 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len) status = spi_claim_bus(spi); if (status) { - debug("SPI DATAFLASH: unable to claim SPI bus\n"); + debug("dataflash: unable to claim SPI bus\n"); return status; } @@ -232,7 +227,7 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len, status = spi_claim_bus(spi); if (status) { - debug("SPI DATAFLASH: unable to claim SPI bus\n"); + debug("dataflash: unable to claim SPI bus\n"); return status; } @@ -290,7 +285,7 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len, status = spi_claim_bus(spi); if (status) { - debug("SPI DATAFLASH: unable to claim SPI bus\n"); + debug("dataflash: unable to claim SPI bus\n"); return status; } @@ -387,7 +382,7 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len, /* Check result of the compare operation */ if (status & (1 << 6)) { - printf("SPI DataFlash: write compare page %u, err %d\n", + printf("dataflash: write compare page %u, err %d\n", pageaddr, status); remaining = 0; status = -EIO; @@ -501,9 +496,10 @@ static struct flash_info dataflash_data[] = { { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS}, }; -static struct flash_info *jedec_probe(struct spi_slave *spi, u8 *id) +static struct flash_info *jedec_probe(struct spi_slave *spi) { int tmp; + uint8_t id[5]; uint32_t jedec; struct flash_info *info; int status; @@ -517,6 +513,11 @@ static struct flash_info *jedec_probe(struct spi_slave *spi, u8 *id) * That's not an error; only rev C and newer chips handle it, and * only Atmel sells these chips. */ + tmp = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); + if (tmp < 0) { + printf("dataflash: error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } if (id[0] != 0x1f) return NULL; @@ -533,7 +534,7 @@ static struct flash_info *jedec_probe(struct spi_slave *spi, u8 *id) if (info->flags & SUP_POW2PS) { status = dataflash_status(spi); if (status < 0) { - debug("SPI DataFlash: status error %d\n", + debug("dataflash: status error %d\n", status); return NULL; } @@ -555,10 +556,8 @@ static struct flash_info *jedec_probe(struct spi_slave *spi, u8 *id) * size (it might be binary) even when we can tell which density * class is involved (legacy chip id scheme). */ - printf("SPI DataFlash: Unsupported flash IDs: "); - printf("manuf %02x, jedec %04x, ext_jedec %04x\n", - id[0], jedec, id[3] << 8 | id[4]); - return NULL; + printf("dataflash: JEDEC id %06x not handled\n", jedec); + return ERR_PTR(-ENODEV); } /* @@ -580,21 +579,15 @@ static int spi_dataflash_probe(struct udevice *dev) struct spi_slave *spi = dev_get_parent_priv(dev); struct spi_flash *spi_flash; struct flash_info *info; - u8 idcode[5]; - int ret, status = 0; + int status; spi_flash = dev_get_uclass_priv(dev); + spi_flash->spi = spi; spi_flash->dev = dev; - ret = spi_claim_bus(spi); - if (ret) - return ret; - - ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); - if (ret) { - printf("SPI DataFlash: Failed to get idcodes\n"); - goto err_read_cmd; - } + status = spi_claim_bus(spi); + if (status) + return status; /* * Try to detect dataflash by JEDEC ID. @@ -603,79 +596,70 @@ static int spi_dataflash_probe(struct udevice *dev) * Both support the security register, though with different * write procedures. */ - info = jedec_probe(spi, idcode); - if (info != NULL) - add_dataflash(dev, info->name, info->nr_pages, - info->pagesize, info->pageoffset, - (info->flags & SUP_POW2PS) ? 'd' : 'c'); - else { - /* - * Older chips support only legacy commands, identifing - * capacity using bits in the status byte. - */ - status = dataflash_status(spi); - if (status <= 0 || status == 0xff) { - printf("SPI DataFlash: read status error %d\n", status); - if (status == 0 || status == 0xff) - status = -ENODEV; - goto err_read_cmd; - } - /* - * if there's a device there, assume it's dataflash. - * board setup should have set spi->max_speed_max to - * match f(car) for continuous reads, mode 0 or 3. - */ - switch (status & 0x3c) { - case 0x0c: /* 0 0 1 1 x x */ - status = add_dataflash(dev, "AT45DB011B", - 512, 264, 9, 0); - break; - case 0x14: /* 0 1 0 1 x x */ - status = add_dataflash(dev, "AT45DB021B", - 1024, 264, 9, 0); - break; - case 0x1c: /* 0 1 1 1 x x */ - status = add_dataflash(dev, "AT45DB041x", - 2048, 264, 9, 0); - break; - case 0x24: /* 1 0 0 1 x x */ - status = add_dataflash(dev, "AT45DB081B", - 4096, 264, 9, 0); - break; - case 0x2c: /* 1 0 1 1 x x */ - status = add_dataflash(dev, "AT45DB161x", - 4096, 528, 10, 0); - break; - case 0x34: /* 1 1 0 1 x x */ - status = add_dataflash(dev, "AT45DB321x", - 8192, 528, 10, 0); - break; - case 0x38: /* 1 1 1 x x x */ - case 0x3c: - status = add_dataflash(dev, "AT45DB642x", - 8192, 1056, 11, 0); - break; - /* obsolete AT45DB1282 not (yet?) supported */ - default: - dev_info(&spi->dev, "unsupported device (%x)\n", - status & 0x3c); - status = -ENODEV; - goto err_read_cmd; - } + info = jedec_probe(spi); + if (IS_ERR(info)) + goto err_jedec_probe; + if (info != NULL) { + status = add_dataflash(dev, info->name, info->nr_pages, + info->pagesize, info->pageoffset, + (info->flags & SUP_POW2PS) ? 'd' : 'c'); + if (status < 0) + goto err_status; } - /* Assign spi data */ - spi_flash->spi = spi; - spi_flash->memory_map = spi->memory_map; - spi_flash->dual_flash = spi->option; + /* + * Older chips support only legacy commands, identifing + * capacity using bits in the status byte. + */ + status = dataflash_status(spi); + if (status <= 0 || status == 0xff) { + printf("dataflash: read status error %d\n", status); + if (status == 0 || status == 0xff) + status = -ENODEV; + goto err_jedec_probe; + } - spi_release_bus(spi); + /* + * if there's a device there, assume it's dataflash. + * board setup should have set spi->max_speed_max to + * match f(car) for continuous reads, mode 0 or 3. + */ + switch (status & 0x3c) { + case 0x0c: /* 0 0 1 1 x x */ + status = add_dataflash(dev, "AT45DB011B", 512, 264, 9, 0); + break; + case 0x14: /* 0 1 0 1 x x */ + status = add_dataflash(dev, "AT45DB021B", 1024, 264, 9, 0); + break; + case 0x1c: /* 0 1 1 1 x x */ + status = add_dataflash(dev, "AT45DB041x", 2048, 264, 9, 0); + break; + case 0x24: /* 1 0 0 1 x x */ + status = add_dataflash(dev, "AT45DB081B", 4096, 264, 9, 0); + break; + case 0x2c: /* 1 0 1 1 x x */ + status = add_dataflash(dev, "AT45DB161x", 4096, 528, 10, 0); + break; + case 0x34: /* 1 1 0 1 x x */ + status = add_dataflash(dev, "AT45DB321x", 8192, 528, 10, 0); + break; + case 0x38: /* 1 1 1 x x x */ + case 0x3c: + status = add_dataflash(dev, "AT45DB642x", 8192, 1056, 11, 0); + break; + /* obsolete AT45DB1282 not (yet?) supported */ + default: + printf("dataflash: unsupported device (%x)\n", status & 0x3c); + status = -ENODEV; + goto err_status; + } - return 0; + return status; -err_read_cmd: +err_status: + spi_free_slave(spi); +err_jedec_probe: spi_release_bus(spi); - return status; } diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index cde4cfb..2463686 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -23,6 +23,7 @@ enum spi_dual_flash { enum spi_nor_option_flags { SNOR_F_SST_WR = BIT(0), SNOR_F_USE_FSR = BIT(1), + SNOR_F_USE_UPAGE = BIT(3), }; #define SPI_FLASH_3B_ADDR_LEN 3 @@ -98,42 +99,45 @@ int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, const void *buf); #endif -#ifdef CONFIG_SPI_FLASH_SPANSION -/* Used for Spansion S25FS-S family flash only. */ -#define CMD_SPANSION_RDAR 0x65 /* Read any device register */ -#define CMD_SPANSION_WRAR 0x71 /* Write any device register */ -#endif -/** - * struct spi_flash_params - SPI/QSPI flash device params structure - * - * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) - * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id]) - * @ext_jedec: Device ext_jedec ID - * @sector_size: Isn't necessarily a sector size from vendor, - * the size listed here is what works with CMD_ERASE_64K - * @nr_sectors: No.of sectors on this device - * @flags: Important param, for flash specific behaviour - */ -struct spi_flash_params { - const char *name; - u32 jedec; - u16 ext_jedec; - u32 sector_size; - u32 nr_sectors; - - u16 flags; -#define SECT_4K BIT(0) -#define E_FSR BIT(1) -#define SST_WR BIT(2) -#define WR_QPP BIT(3) -#define RD_QUAD BIT(4) -#define RD_DUAL BIT(5) -#define RD_QUADIO BIT(6) -#define RD_DUALIO BIT(7) +#define JEDEC_MFR(info) ((info)->id[0]) +#define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) +#define JEDEC_EXT(info) (((info)->id[3]) << 8 | ((info)->id[4])) +#define SPI_FLASH_MAX_ID_LEN 6 + +struct spi_flash_info { + /* Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) */ + const char *name; + + /* + * This array stores the ID bytes. + * The first three bytes are the JEDIC ID. + * JEDEC ID zero means "no ID" (mostly older chips). + */ + u8 id[SPI_FLASH_MAX_ID_LEN]; + u8 id_len; + + /* + * The size listed here is what works with SPINOR_OP_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + u32 sector_size; + u32 n_sectors; + + u16 page_size; + + u16 flags; +#define SECT_4K BIT(0) /* CMD_ERASE_4K works uniformly */ +#define E_FSR BIT(1) /* use flag status register for */ +#define SST_WR BIT(2) /* use SST byte/word programming */ +#define WR_QPP BIT(3) /* use Quad Page Program */ +#define RD_QUAD BIT(4) /* use Quad Read */ +#define RD_DUAL BIT(5) /* use Dual Read */ +#define RD_QUADIO BIT(6) /* use Quad IO Read */ +#define RD_DUALIO BIT(7) /* use Dual IO Read */ #define RD_FULL (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO) }; -extern const struct spi_flash_params spi_flash_params_table[]; +extern const struct spi_flash_info spi_flash_ids[]; /* Send a single-byte command to the device and read the response */ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); @@ -182,7 +186,7 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) * - SPI claim * - spi_flash_cmd_write_enable * - spi_flash_cmd_write - * - spi_flash_cmd_wait_ready + * - spi_flash_wait_till_ready * - SPI release */ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c deleted file mode 100644 index 5b50114..0000000 --- a/drivers/mtd/spi/sf_params.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * SPI flash Params table - * - * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <spi.h> -#include <spi_flash.h> - -#include "sf_internal.h" - -/* SPI/QSPI flash device params structure */ -const struct spi_flash_params spi_flash_params_table[] = { -#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ - {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, SECT_4K}, - {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, SECT_4K}, - {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, SECT_4K}, - {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, SECT_4K}, - {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, SECT_4K}, - {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, SECT_4K}, - {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, SECT_4K}, - {"AT25DF321A", 0x1f4701, 0x0, 64 * 1024, 64, SECT_4K}, - {"AT25DF321", 0x1f4700, 0x0, 64 * 1024, 64, SECT_4K}, - {"AT26DF081A", 0x1f4501, 0x0, 64 * 1024, 16, SECT_4K}, -#endif -#ifdef CONFIG_SPI_FLASH_EON /* EON */ - {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0}, - {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, SECT_4K}, - {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0}, - {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0}, -#endif -#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ - {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, SECT_4K}, - {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, SECT_4K}, -#endif -#ifdef CONFIG_SPI_FLASH_ISSI /* ISSI */ - {"IS25LP032", 0x9d6016, 0x0, 64 * 1024, 64, 0}, - {"IS25LP064", 0x9d6017, 0x0, 64 * 1024, 128, 0}, - {"IS25LP128", 0x9d6018, 0x0, 64 * 1024, 256, 0}, -#endif -#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ - {"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, 0}, - {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0}, - {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0}, - {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0}, - {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0}, - {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0}, - {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP}, - {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP}, - {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP}, - {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP}, -#endif -#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ - {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0}, - {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0}, - {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0}, - {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0}, - {"S25FL116K", 0x014015, 0x0, 64 * 1024, 128, 0}, - {"S25FL164K", 0x014017, 0x0140, 64 * 1024, 128, 0}, - {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, RD_FULL | WR_QPP}, - {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, RD_FULL | WR_QPP}, - {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, RD_FULL | WR_QPP}, - {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, RD_FULL | WR_QPP}, - {"S25FL128S_256K", 0x012018, 0x4d00, 256 * 1024, 64, RD_FULL | WR_QPP}, - {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, RD_FULL | WR_QPP}, - {"S25FL256S_256K", 0x010219, 0x4d00, 256 * 1024, 128, RD_FULL | WR_QPP}, - {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, RD_FULL | WR_QPP}, - {"S25FS512S", 0x010220, 0x4D00, 128 * 1024, 512, RD_FULL | WR_QPP}, - {"S25FL512S_256K", 0x010220, 0x4d00, 256 * 1024, 256, RD_FULL | WR_QPP}, - {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL | WR_QPP}, - {"S25FL512S_512K", 0x010220, 0x4f00, 256 * 1024, 256, RD_FULL | WR_QPP}, -#endif -#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ - {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0}, - {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0}, - {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0}, - {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0}, - {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0}, - {"M25PE16", 0x208015, 0x1000, 64 * 1024, 32, 0}, - {"M25PX16", 0x207115, 0x1000, 64 * 1024, 32, RD_QUAD | RD_DUAL}, - {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0}, - {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0}, - {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0}, - {"M25PX64", 0x207117, 0x0, 64 * 1024, 128, SECT_4K}, - {"N25Q016A", 0x20bb15, 0x0, 64 * 1024, 32, SECT_4K}, - {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K}, - {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K}, - {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K}, - {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K}, - {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP}, - {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP}, - {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K}, - {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K}, - {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K}, - {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K}, - {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K}, - {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K}, -#endif -#ifdef CONFIG_SPI_FLASH_SST /* SST */ - {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WR}, - {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WR}, - {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WR}, - {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WR}, - {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, SECT_4K}, - {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WR}, - {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WR}, - {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WR}, - {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WR}, - {"SST25WF040B", 0x621613, 0x0, 64 * 1024, 8, SECT_4K}, - {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WR}, -#endif -#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ - {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0}, - {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0}, - {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0}, - {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, SECT_4K}, - {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, SECT_4K}, - {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, SECT_4K}, - {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, SECT_4K}, - {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K}, - {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K}, -#endif - {}, /* Empty entry to terminate the list */ - /* - * Note: - * Below paired flash devices has similar spi_flash params. - * (S25FL129P_64K, S25FL128S_64K) - * (W25Q80BL, W25Q80BV) - * (W25Q16CL, W25Q16DV) - * (W25Q32BV, W25Q32FV_SPI) - * (W25Q64CV, W25Q64FV_SPI) - * (W25Q128BV, W25Q128FV_SPI) - * (W25Q32DW, W25Q32FV_QPI) - * (W25Q64DW, W25Q64FV_QPI) - * (W25Q128FW, W25Q128FV_QPI) - */ -}; diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 7f6e9ae..94c0b00 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -144,7 +144,7 @@ static int write_evcr(struct spi_flash *flash, u8 evcr) #endif #ifdef CONFIG_SPI_FLASH_BAR -static int spi_flash_write_bar(struct spi_flash *flash, u32 offset) +static int write_bar(struct spi_flash *flash, u32 offset) { u8 cmd, bank_sel; int ret; @@ -165,7 +165,7 @@ bar_end: return flash->bank_curr; } -static int spi_flash_read_bar(struct spi_flash *flash, u8 idcode0) +static int read_bar(struct spi_flash *flash, const struct spi_flash_info *info) { u8 curr_bank = 0; int ret; @@ -173,7 +173,7 @@ static int spi_flash_read_bar(struct spi_flash *flash, u8 idcode0) if (flash->size <= SPI_FLASH_16MB_BOUN) goto bar_end; - switch (idcode0) { + switch (JEDEC_MFR(info)) { case SPI_FLASH_CFI_MFR_SPANSION: flash->bank_read_cmd = CMD_BANKADDR_BRRD; flash->bank_write_cmd = CMD_BANKADDR_BRWR; @@ -199,15 +199,13 @@ bar_end: #ifdef CONFIG_SF_DUAL_FLASH static void spi_flash_dual(struct spi_flash *flash, u32 *addr) { - struct spi_slave *spi = flash->spi; - switch (flash->dual_flash) { case SF_DUAL_STACKED_FLASH: if (*addr >= (flash->size >> 1)) { *addr -= flash->size >> 1; - spi->flags |= SPI_XFER_U_PAGE; + flash->flags |= SNOR_F_USE_UPAGE; } else { - spi->flags &= ~SPI_XFER_U_PAGE; + flash->flags &= ~SNOR_F_USE_UPAGE; } break; case SF_DUAL_PARALLEL_FLASH: @@ -262,8 +260,8 @@ static int spi_flash_ready(struct spi_flash *flash) return sr && fsr; } -static int spi_flash_cmd_wait_ready(struct spi_flash *flash, - unsigned long timeout) +static int spi_flash_wait_till_ready(struct spi_flash *flash, + unsigned long timeout) { unsigned long timebase; int ret; @@ -311,7 +309,7 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, return ret; } - ret = spi_flash_cmd_wait_ready(flash, timeout); + ret = spi_flash_wait_till_ready(flash, timeout); if (ret < 0) { debug("SF: write %s timed out\n", timeout == SPI_FLASH_PROG_TIMEOUT ? @@ -353,7 +351,7 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) spi_flash_dual(flash, &erase_addr); #endif #ifdef CONFIG_SPI_FLASH_BAR - ret = spi_flash_write_bar(flash, erase_addr); + ret = write_bar(flash, erase_addr); if (ret < 0) return ret; #endif @@ -404,7 +402,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, spi_flash_dual(flash, &write_addr); #endif #ifdef CONFIG_SPI_FLASH_BAR - ret = spi_flash_write_bar(flash, write_addr); + ret = write_bar(flash, write_addr); if (ret < 0) return ret; #endif @@ -508,7 +506,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, spi_flash_dual(flash, &read_addr); #endif #ifdef CONFIG_SPI_FLASH_BAR - ret = spi_flash_write_bar(flash, read_addr); + ret = write_bar(flash, read_addr); if (ret < 0) return ret; bank_sel = flash->bank_curr; @@ -560,7 +558,7 @@ static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) if (ret) return ret; - return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + return spi_flash_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT); } int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, @@ -608,7 +606,7 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, break; } - ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + ret = spi_flash_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT); if (ret) break; @@ -924,9 +922,35 @@ static int micron_quad_enable(struct spi_flash *flash) } #endif -static int set_quad_mode(struct spi_flash *flash, u8 idcode0) +static const struct spi_flash_info *spi_flash_read_id(struct spi_flash *flash) { - switch (idcode0) { + int tmp; + u8 id[SPI_FLASH_MAX_ID_LEN]; + const struct spi_flash_info *info; + + tmp = spi_flash_cmd(flash->spi, CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN); + if (tmp < 0) { + printf("SF: error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + + info = spi_flash_ids; + for (; info->name != NULL; info++) { + if (info->id_len) { + if (!memcmp(info->id, id, info->id_len)) + return info; + } + } + + printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n", + id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); +} + +static int set_quad_mode(struct spi_flash *flash, + const struct spi_flash_info *info) +{ + switch (JEDEC_MFR(info)) { #ifdef CONFIG_SPI_FLASH_MACRONIX case SPI_FLASH_CFI_MFR_MACRONIX: return macronix_quad_enable(flash); @@ -941,7 +965,8 @@ static int set_quad_mode(struct spi_flash *flash, u8 idcode0) return micron_quad_enable(flash); #endif default: - printf("SF: Need set QEB func for %02x flash\n", idcode0); + printf("SF: Need set QEB func for %02x flash\n", + JEDEC_MFR(info)); return -1; } } @@ -971,138 +996,28 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) } #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ -#ifdef CONFIG_SPI_FLASH_SPANSION -static int spansion_s25fss_disable_4KB_erase(struct spi_slave *spi) -{ - u8 cmd[4]; - u32 offset = 0x800004; /* CR3V register offset */ - u8 cr3v; - int ret; - - cmd[0] = CMD_SPANSION_RDAR; - cmd[1] = offset >> 16; - cmd[2] = offset >> 8; - cmd[3] = offset >> 0; - - ret = spi_flash_cmd_read(spi, cmd, 4, &cr3v, 1); - if (ret) - return -EIO; - /* CR3V bit3: 4-KB Erase */ - if (cr3v & 0x8) - return 0; - - cmd[0] = CMD_SPANSION_WRAR; - cr3v |= 0x8; - ret = spi_flash_cmd_write(spi, cmd, 4, &cr3v, 1); - if (ret) - return -EIO; - - cmd[0] = CMD_SPANSION_RDAR; - ret = spi_flash_cmd_read(spi, cmd, 4, &cr3v, 1); - if (ret) - return -EIO; - if (!(cr3v & 0x8)) - return -EFAULT; - - return 0; -} -#endif - int spi_flash_scan(struct spi_flash *flash) { struct spi_slave *spi = flash->spi; - const struct spi_flash_params *params; - u16 jedec, ext_jedec; - u8 idcode[5]; - int ret; - - /* Read the ID codes */ - ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); - if (ret) { - printf("SF: Failed to get idcodes\n"); - return ret; - } - -#ifdef DEBUG - printf("SF: Got idcodes\n"); - print_buffer(0, idcode, 1, sizeof(idcode), 0); -#endif - - jedec = idcode[1] << 8 | idcode[2]; - ext_jedec = idcode[3] << 8 | idcode[4]; - - /* Validate params from spi_flash_params table */ - params = spi_flash_params_table; - for (; params->name != NULL; params++) { - if ((params->jedec >> 16) == idcode[0]) { - if ((params->jedec & 0xFFFF) == jedec) { - if (params->ext_jedec == 0) - break; - else if (params->ext_jedec == ext_jedec) - break; - } - } - } - - if (!params->name) { - printf("SF: Unsupported flash IDs: "); - printf("manuf %02x, jedec %04x, ext_jedec %04x\n", - idcode[0], jedec, ext_jedec); - return -EPROTONOSUPPORT; - } - -#ifdef CONFIG_SPI_FLASH_SPANSION - /* - * The S25FS-S family physical sectors may be configured as a - * hybrid combination of eight 4-kB parameter sectors - * at the top or bottom of the address space with all - * but one of the remaining sectors being uniform size. - * The Parameter Sector Erase commands (20h or 21h) must - * be used to erase the 4-kB parameter sectors individually. - * The Sector (uniform sector) Erase commands (D8h or DCh) - * must be used to erase any of the remaining - * sectors, including the portion of highest or lowest address - * sector that is not overlaid by the parameter sectors. - * The uniform sector erase command has no effect on parameter sectors. - */ - if ((jedec == 0x0219 || (jedec == 0x0220)) && - (ext_jedec & 0xff00) == 0x4d00) { - int ret; - u8 id[6]; - - /* Read the ID codes again, 6 bytes */ - ret = spi_flash_cmd(flash->spi, CMD_READ_ID, id, sizeof(id)); - if (ret) - return -EIO; + const struct spi_flash_info *info = NULL; + int ret = -1; - ret = memcmp(id, idcode, 5); - if (ret) - return -EIO; + info = spi_flash_read_id(flash); + if (IS_ERR_OR_NULL(info)) + return -ENOENT; - /* 0x81: S25FS-S family 0x80: S25FL-S family */ - if (id[5] == 0x81) { - ret = spansion_s25fss_disable_4KB_erase(spi); - if (ret) - return ret; - } - } -#endif /* Flash powers up read-only, so clear BP# bits */ - if (idcode[0] == SPI_FLASH_CFI_MFR_ATMEL || - idcode[0] == SPI_FLASH_CFI_MFR_MACRONIX || - idcode[0] == SPI_FLASH_CFI_MFR_SST) + if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_ATMEL || + JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX || + JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST) write_sr(flash, 0); - /* Assign spi data */ - flash->name = params->name; + flash->name = info->name; flash->memory_map = spi->memory_map; - flash->dual_flash = spi->option; - /* Assign spi flash flags */ - if (params->flags & SST_WR) + if (info->flags & SST_WR) flash->flags |= SNOR_F_SST_WR; - /* Assign spi_flash ops */ #ifndef CONFIG_DM_SPI_FLASH flash->write = spi_flash_cmd_write_ops; #if defined(CONFIG_SPI_FLASH_SST) @@ -1117,39 +1032,33 @@ int spi_flash_scan(struct spi_flash *flash) flash->read = spi_flash_cmd_read_ops; #endif - /* lock hooks are flash specific - assign them based on idcode0 */ - switch (idcode[0]) { #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST) - case SPI_FLASH_CFI_MFR_STMICRO: - case SPI_FLASH_CFI_MFR_SST: + /* NOR protection support for STmicro/Micron chips and similar */ + if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_STMICRO || + JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST) { flash->flash_lock = stm_lock; flash->flash_unlock = stm_unlock; flash->flash_is_locked = stm_is_locked; -#endif - break; - default: - debug("SF: Lock ops not supported for %02x flash\n", idcode[0]); } +#endif /* Compute the flash size */ flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0; + flash->page_size = info->page_size; /* * The Spansion S25FL032P and S25FL064P have 256b pages, yet use the * 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with * the 0x4d00 Extended JEDEC code have 512b pages. All of the others * have 256b pages. */ - if (ext_jedec == 0x4d00) { - if ((jedec == 0x0215) || (jedec == 0x216) || (jedec == 0x220)) - flash->page_size = 256; - else + if (JEDEC_EXT(info) == 0x4d00) { + if ((JEDEC_ID(info) != 0x0215) && + (JEDEC_ID(info) != 0x0216)) flash->page_size = 512; - } else { - flash->page_size = 256; } flash->page_size <<= flash->shift; - flash->sector_size = params->sector_size << flash->shift; - flash->size = flash->sector_size * params->nr_sectors << flash->shift; + flash->sector_size = info->sector_size << flash->shift; + flash->size = flash->sector_size * info->n_sectors << flash->shift; #ifdef CONFIG_SF_DUAL_FLASH if (flash->dual_flash & SF_DUAL_STACKED_FLASH) flash->size <<= 1; @@ -1157,7 +1066,7 @@ int spi_flash_scan(struct spi_flash *flash) #ifdef CONFIG_SPI_FLASH_USE_4K_SECTORS /* Compute erase sector and command */ - if (params->flags & SECT_4K) { + if (info->flags & SECT_4K) { flash->erase_cmd = CMD_ERASE_4K; flash->erase_size = 4096 << flash->shift; } else @@ -1174,13 +1083,13 @@ int spi_flash_scan(struct spi_flash *flash) flash->read_cmd = CMD_READ_ARRAY_FAST; if (spi->mode & SPI_RX_SLOW) flash->read_cmd = CMD_READ_ARRAY_SLOW; - else if (spi->mode & SPI_RX_QUAD && params->flags & RD_QUAD) + else if (spi->mode & SPI_RX_QUAD && info->flags & RD_QUAD) flash->read_cmd = CMD_READ_QUAD_OUTPUT_FAST; - else if (spi->mode & SPI_RX_DUAL && params->flags & RD_DUAL) + else if (spi->mode & SPI_RX_DUAL && info->flags & RD_DUAL) flash->read_cmd = CMD_READ_DUAL_OUTPUT_FAST; /* Look for write commands */ - if (params->flags & WR_QPP && spi->mode & SPI_TX_QUAD) + if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD) flash->write_cmd = CMD_QUAD_PAGE_PROGRAM; else /* Go for default supported write cmd */ @@ -1190,9 +1099,10 @@ int spi_flash_scan(struct spi_flash *flash) if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) || (flash->read_cmd == CMD_READ_QUAD_IO_FAST) || (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) { - ret = set_quad_mode(flash, idcode[0]); + ret = set_quad_mode(flash, info); if (ret) { - debug("SF: Fail to set QEB for %02x\n", idcode[0]); + debug("SF: Fail to set QEB for %02x\n", + JEDEC_MFR(info)); return -EINVAL; } } @@ -1217,18 +1127,18 @@ int spi_flash_scan(struct spi_flash *flash) } #ifdef CONFIG_SPI_FLASH_STMICRO - if (params->flags & E_FSR) + if (info->flags & E_FSR) flash->flags |= SNOR_F_USE_FSR; #endif /* Configure the BAR - discover bank cmds and read current bank */ #ifdef CONFIG_SPI_FLASH_BAR - ret = spi_flash_read_bar(flash, idcode[0]); + ret = read_bar(flash, info); if (ret < 0) return ret; #endif -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) ret = spi_flash_decode_fdt(gd->fdt_blob, flash); if (ret) { debug("SF: FDT decode error\n"); diff --git a/drivers/mtd/spi/spi_flash_ids.c b/drivers/mtd/spi/spi_flash_ids.c new file mode 100644 index 0000000..edca94e --- /dev/null +++ b/drivers/mtd/spi/spi_flash_ids.c @@ -0,0 +1,184 @@ +/* + * SPI Flash ID's. + * + * Copyright (C) 2016 Jagan Teki <jagan@openedev.com> + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spi.h> +#include <spi_flash.h> + +#include "sf_internal.h" + +/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 16) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +const struct spi_flash_info spi_flash_ids[] = { +#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ + {"at45db011d", INFO(0x1f2200, 0x0, 64 * 1024, 4, SECT_4K) }, + {"at45db021d", INFO(0x1f2300, 0x0, 64 * 1024, 8, SECT_4K) }, + {"at45db041d", INFO(0x1f2400, 0x0, 64 * 1024, 8, SECT_4K) }, + {"at45db081d", INFO(0x1f2500, 0x0, 64 * 1024, 16, SECT_4K) }, + {"at45db161d", INFO(0x1f2600, 0x0, 64 * 1024, 32, SECT_4K) }, + {"at45db321d", INFO(0x1f2700, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at45db641d", INFO(0x1f2800, 0x0, 64 * 1024, 128, SECT_4K) }, + {"at25df321a", INFO(0x1f4701, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at25df321", INFO(0x1f4700, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at26df081a", INFO(0x1f4501, 0x0, 64 * 1024, 16, SECT_4K) }, +#endif +#ifdef CONFIG_SPI_FLASH_EON /* EON */ + {"en25q32b", INFO(0x1c3016, 0x0, 64 * 1024, 64, 0) }, + {"en25q64", INFO(0x1c3017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"en25q128b", INFO(0x1c3018, 0x0, 64 * 1024, 256, 0) }, + {"en25s64", INFO(0x1c3817, 0x0, 64 * 1024, 128, 0) }, +#endif +#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ + {"gd25q64b", INFO(0xc84017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"gd25lq32", INFO(0xc86016, 0x0, 64 * 1024, 64, SECT_4K) }, +#endif +#ifdef CONFIG_SPI_FLASH_ISSI /* ISSI */ + {"is25lp032", INFO(0x9d6016, 0x0, 64 * 1024, 64, 0) }, + {"is25lp064", INFO(0x9d6017, 0x0, 64 * 1024, 128, 0) }, + {"is25lp128", INFO(0x9d6018, 0x0, 64 * 1024, 256, 0) }, +#endif +#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ + {"mx25l2006e", INFO(0xc22012, 0x0, 64 * 1024, 4, 0) }, + {"mx25l4005", INFO(0xc22013, 0x0, 64 * 1024, 8, 0) }, + {"mx25l8005", INFO(0xc22014, 0x0, 64 * 1024, 16, 0) }, + {"mx25l1605d", INFO(0xc22015, 0x0, 64 * 1024, 32, 0) }, + {"mx25l3205d", INFO(0xc22016, 0x0, 64 * 1024, 64, 0) }, + {"mx25l6405d", INFO(0xc22017, 0x0, 64 * 1024, 128, 0) }, + {"mx25l12805", INFO(0xc22018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"mx25l25635f", INFO(0xc22019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP) }, + {"mx25l51235f", INFO(0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx25l12855e", INFO(0xc22618, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"mx66u51235f", INFO(0xc2253a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx66l1g45g", INFO(0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ + {"s25fl008a", INFO(0x010213, 0x0, 64 * 1024, 16, 0) }, + {"s25fl016a", INFO(0x010214, 0x0, 64 * 1024, 32, 0) }, + {"s25fl032a", INFO(0x010215, 0x0, 64 * 1024, 64, 0) }, + {"s25fl064a", INFO(0x010216, 0x0, 64 * 1024, 128, 0) }, + {"s25fl116k", INFO(0x014015, 0x0, 64 * 1024, 128, 0) }, + {"s25fl164k", INFO(0x014017, 0x0140, 64 * 1024, 128, 0) }, + {"s25fl128p_256k", INFO(0x012018, 0x0300, 256 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl128p_64k", INFO(0x012018, 0x0301, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, RD_FULL | WR_QPP) }, + {"s25fl128s_256k", INFO(0x012018, 0x4d00, 256 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl128s_64k", INFO(0x012018, 0x4d01, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl256s_256k", INFO(0x010219, 0x4d00, 256 * 1024, 128, RD_FULL | WR_QPP) }, + {"s25fl256s_64k", INFO(0x010219, 0x4d01, 64 * 1024, 512, RD_FULL | WR_QPP) }, + {"s25fs256s_64k", INFO6(0x010219, 0x4d0181, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"s25fs512s", INFO6(0x010220, 0x4d0081, 128 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"s25fl512s_256k", INFO(0x010220, 0x4d00, 256 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl512s_64k", INFO(0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"s25fl512s_512k", INFO(0x010220, 0x4f00, 256 * 1024, 256, RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ + {"m25p10", INFO(0x202011, 0x0, 32 * 1024, 4, 0) }, + {"m25p20", INFO(0x202012, 0x0, 64 * 1024, 4, 0) }, + {"m25p40", INFO(0x202013, 0x0, 64 * 1024, 8, 0) }, + {"m25p80", INFO(0x202014, 0x0, 64 * 1024, 16, 0) }, + {"m25p16", INFO(0x202015, 0x0, 64 * 1024, 32, 0) }, + {"m25pE16", INFO(0x208015, 0x1000, 64 * 1024, 32, 0) }, + {"m25pX16", INFO(0x207115, 0x1000, 64 * 1024, 32, RD_QUAD | RD_DUAL) }, + {"m25p32", INFO(0x202016, 0x0, 64 * 1024, 64, 0) }, + {"m25p64", INFO(0x202017, 0x0, 64 * 1024, 128, 0) }, + {"m25p128", INFO(0x202018, 0x0, 256 * 1024, 64, 0) }, + {"m25pX64", INFO(0x207117, 0x0, 64 * 1024, 128, SECT_4K) }, + {"n25q016a", INFO(0x20bb15, 0x0, 64 * 1024, 32, SECT_4K) }, + {"n25q32", INFO(0x20ba16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q32a", INFO(0x20bb16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q64", INFO(0x20ba17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q64a", INFO(0x20bb17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q128", INFO(0x20ba18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"n25q128a", INFO(0x20bb18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"n25q256", INFO(0x20ba19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q256a", INFO(0x20bb19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q512", INFO(0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q512a", INFO(0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q1024", INFO(0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q1024a", INFO(0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt25qu02g", INFO(0x20bb22, 0x0, 64 * 1024, 4096, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt25ql02g", INFO(0x20ba22, 0x0, 64 * 1024, 4096, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_FLASH_SST /* SST */ + {"sst25vf040b", INFO(0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WR) }, + {"sst25vf080b", INFO(0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WR) }, + {"sst25vf016b", INFO(0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WR) }, + {"sst25vf032b", INFO(0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WR) }, + {"sst25vf064c", INFO(0xbf254b, 0x0, 64 * 1024, 128, SECT_4K) }, + {"sst25wf512", INFO(0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WR) }, + {"sst25wf010", INFO(0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WR) }, + {"sst25wf020", INFO(0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WR) }, + {"sst25wf040", INFO(0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WR) }, + {"sst25wf040b", INFO(0x621613, 0x0, 64 * 1024, 8, SECT_4K) }, + {"sst25wf080", INFO(0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WR) }, +#endif +#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ + {"w25p80", INFO(0xef2014, 0x0, 64 * 1024, 16, 0) }, + {"w25p16", INFO(0xef2015, 0x0, 64 * 1024, 32, 0) }, + {"w25p32", INFO(0xef2016, 0x0, 64 * 1024, 64, 0) }, + {"w25x40", INFO(0xef3013, 0x0, 64 * 1024, 8, SECT_4K) }, + {"w25x16", INFO(0xef3015, 0x0, 64 * 1024, 32, SECT_4K) }, + {"w25x32", INFO(0xef3016, 0x0, 64 * 1024, 64, SECT_4K) }, + {"w25x64", INFO(0xef3017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"w25q80bl", INFO(0xef4014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q16cl", INFO(0xef4015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q32bv", INFO(0xef4016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q64cv", INFO(0xef4017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q128bv", INFO(0xef4018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q256", INFO(0xef4019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q80bw", INFO(0xef5014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q16dw", INFO(0xef6015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q32dw", INFO(0xef6016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q64dw", INFO(0xef6017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q128fw", INFO(0xef6018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K) }, +#endif + {}, /* Empty entry to terminate the list */ + /* + * Note: + * Below paired flash devices has similar spi_flash params. + * (s25fl129p_64k, s25fl128s_64k) + * (w25q80bl, w25q80bv) + * (w25q16cl, w25q16dv) + * (w25q32bv, w25q32fv_spi) + * (w25q64cv, w25q64fv_spi) + * (w25q128bv, w25q128fv_spi) + * (w25q32dw, w25q32fv_qpi) + * (w25q64dw, w25q64fv_qpi) + * (w25q128fw, w25q128fv_qpi) + */ +}; diff --git a/drivers/mtd/spi/sunxi_spi_spl.c b/drivers/mtd/spi/sunxi_spi_spl.c index 67c7edd..7502314 100644 --- a/drivers/mtd/spi/sunxi_spi_spl.c +++ b/drivers/mtd/spi/sunxi_spi_spl.c @@ -158,9 +158,10 @@ static void spi0_disable_clock(void) (1 << AHB_RESET_SPI0_SHIFT)); } -static int spi0_init(void) +static void spi0_init(void) { unsigned int pin_function = SUNXI_GPC_SPI0; + if (IS_ENABLED(CONFIG_MACH_SUN50I)) pin_function = SUN50I_GPC_SPI0; |