From b199489d37b21c5e294f95bf265acc5dde3fc3a2 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:37 +0800 Subject: mtd: spi-nor: add the framework for SPI NOR This patch cloned most of the m25p80.c. In theory, it adds a new spi-nor layer. Before this patch, the layer is like: MTD ------------------------ m25p80 ------------------------ spi bus driver ------------------------ SPI NOR chip After this patch, the layer is like: MTD ------------------------ spi-nor ------------------------ m25p80 ------------------------ spi bus driver ------------------------ SPI NOR chip With the spi-nor controller driver(Freescale Quadspi), it looks like: MTD ------------------------ spi-nor ------------------------ fsl-quadspi ------------------------ SPI NOR chip New APIs: spi_nor_scan: used to scan a spi-nor flash. Signed-off-by: Huang Shijie Acked-by: Marek Vasut [Brian: rebased to include additional m25p_ids[] entry] Signed-off-by: Brian Norris --- drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 1 + drivers/mtd/spi-nor/Kconfig | 6 + drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/spi-nor.c | 1088 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1098 insertions(+) create mode 100644 drivers/mtd/spi-nor/Kconfig create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/spi-nor.c (limited to 'drivers') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5d49a2129618..94b821042d9d 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig" source "drivers/mtd/lpddr/Kconfig" +source "drivers/mtd/spi-nor/Kconfig" + source "drivers/mtd/ubi/Kconfig" endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4cfb31e6c966..40fd15344387 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig new file mode 100644 index 000000000000..41591af67aca --- /dev/null +++ b/drivers/mtd/spi-nor/Kconfig @@ -0,0 +1,6 @@ +config MTD_SPI_NOR_BASE + bool "the framework for SPI-NOR support" + depends on MTD + help + This is the framework for the SPI NOR which can be used by the SPI + device drivers and the SPI-NOR device driver. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 000000000000..7dfe1f9b6940 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 000000000000..50b929095bdb --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,1088 @@ +/* + * Cloned most of the code from the m25p80.c + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Define max times to check status register before we give up. */ +#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ + +#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) + +/* + * Read the status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_sr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, OPCODE_RDSR, &val, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + + return val; +} + +/* + * Read configuration register, returning its value in the + * location. Return the configuration register value. + * Returns negative if error occured. + */ +static int read_cr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, OPCODE_RDCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; + } + + return val; +} + +/* + * Dummy Cycle calculation for different type of read. + * It can be used to support more commands with + * different dummy cycle requirements. + */ +static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) +{ + switch (nor->flash_read) { + case SPI_NOR_FAST: + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: + return 1; + case SPI_NOR_NORMAL: + return 0; + } + return 0; +} + +/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +static inline int write_sr(struct spi_nor *nor, u8 val) +{ + nor->cmd_buf[0] = val; + return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); +} + +/* + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static inline int write_enable(struct spi_nor *nor) +{ + return nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); +} + +/* + * Send write disble instruction to the chip. + */ +static inline int write_disable(struct spi_nor *nor) +{ + return nor->write_reg(nor, OPCODE_WRDI, NULL, 0, 0); +} + +static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/* Enable/disable 4-byte addressing mode. */ +static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) +{ + int status; + bool need_wren = false; + u8 cmd; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; + case CFI_MFR_MACRONIX: + case 0xEF /* winbond */: + if (need_wren) + write_enable(nor); + + cmd = enable ? OPCODE_EN4B : OPCODE_EX4B; + status = nor->write_reg(nor, cmd, NULL, 0, 0); + if (need_wren) + write_disable(nor); + + return status; + default: + /* Spansion style */ + nor->cmd_buf[0] = enable << 7; + return nor->write_reg(nor, OPCODE_BRWR, nor->cmd_buf, 1, 0); + } +} + +static int spi_nor_wait_till_ready(struct spi_nor *nor) +{ + unsigned long deadline; + int sr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) + break; + else if (!(sr & SR_WIP)) + return 0; + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + +/* + * Service routine to read status register until ready, or timeout occurs. + * Returns non-zero if error. + */ +static int wait_till_ready(struct spi_nor *nor) +{ + return nor->wait_till_ready(nor); +} + +/* + * Erase the whole flash memory + * + * Returns 0 if successful, non-zero otherwise. + */ +static int erase_chip(struct spi_nor *nor) +{ + int ret; + + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + write_enable(nor); + + return nor->write_reg(nor, OPCODE_CHIP_ERASE, NULL, 0, 0); +} + +static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + int ret = 0; + + mutex_lock(&nor->lock); + + if (nor->prepare) { + ret = nor->prepare(nor, ops); + if (ret) { + dev_err(nor->dev, "failed in the preparation.\n"); + mutex_unlock(&nor->lock); + return ret; + } + } + return ret; +} + +static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + if (nor->unprepare) + nor->unprepare(nor, ops); + mutex_unlock(&nor->lock); +} + +/* + * Erase an address range on the nor chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 addr, len; + uint32_t rem; + int ret; + + dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, + (long long)instr->len); + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE); + if (ret) + return ret; + + /* whole-chip erase? */ + if (len == mtd->size) { + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ + + /* "sector"-at-a-time erase */ + } else { + while (len) { + if (nor->erase(nor, addr)) { + ret = -EIO; + goto erase_err; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + } + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + instr->state = MTD_ERASE_FAILED; + return ret; +} + +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset < mtd->size - (mtd->size / 2)) + status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 4)) + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + else if (offset < mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset < mtd->size - (mtd->size / 32)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 64)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset+len > mtd->size - (mtd->size / 64)) + status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); + else if (offset+len > mtd->size - (mtd->size / 32)) + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else if (offset+len > mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 4)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset+len > mtd->size - (mtd->size / 2)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); + return ret; +} + +struct flash_info { + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + u16 addr_width; + + u16 flags; +#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ +#define SST_WRITE 0x04 /* use SST byte programming */ +#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ +#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ +#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +}; + +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .jedec_id = (_jedec_id), \ + .ext_id = (_ext_id), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), \ + }) + +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = (_flags), \ + }) + +/* NOTE: double check command sets and memory organization when you add + * more nor chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +const struct spi_device_id spi_nor_ids[] = { + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, + + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, + + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + + { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, + + /* EON -- en25xxx */ + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, + + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + + /* GigaDevice */ + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, + + /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + + /* Micron */ + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, + { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, + { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, + { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, + { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, + { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, + { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, + { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, + { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, + { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, + { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, + { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, + { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, + { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, + { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, + { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, + { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, + { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, + { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, + { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, + + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, + { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, + { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, + + { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, + { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, + { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, + + { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, + { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, + + /* Catalyst / On Semiconductor -- non-JEDEC */ + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { }, +}; + +static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) +{ + int tmp; + u8 id[5]; + u32 jedec; + u16 ext_jedec; + struct flash_info *info; + + tmp = nor->read_reg(nor, OPCODE_RDID, id, 5); + if (tmp < 0) { + dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + ext_jedec = id[3] << 8 | id[4]; + + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { + info = (void *)spi_nor_ids[tmp].driver_data; + if (info->jedec_id == jedec) { + if (info->ext_id == 0 || info->ext_id == ext_jedec) + return &spi_nor_ids[tmp]; + } + } + dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); + return ERR_PTR(-ENODEV); +} + +static const struct spi_device_id *jedec_probe(struct spi_nor *nor) +{ + return nor->read_id(nor); +} + +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); + if (ret) + return ret; + + ret = nor->read(nor, from, len, retlen, buf); + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; +} + +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t actual; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + write_enable(nor); + + nor->sst_write_second = false; + + actual = to % 2; + /* Start write from odd address. */ + if (actual) { + nor->program_opcode = OPCODE_BP; + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + } + to += actual; + + /* Write out most of the data here. */ + for (; actual < len - 1; actual += 2) { + nor->program_opcode = OPCODE_AAI_WP; + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; + nor->sst_write_second = true; + } + nor->sst_write_second = false; + + write_disable(nor); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(nor); + + nor->program_opcode = OPCODE_BP; + nor->write(nor, to, 1, retlen, buf + actual); + + ret = wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); + } +time_out: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; +} + +/* + * Write an address range to the nor chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. + */ +static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 page_offset, page_size, i; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto write_err; + + write_enable(nor); + + page_offset = to & (nor->page_size - 1); + + /* do all the bytes fit onto one page? */ + if (page_offset + len <= nor->page_size) { + nor->write(nor, to, len, retlen, buf); + } else { + /* the size of data remaining on the first page */ + page_size = nor->page_size - page_offset; + nor->write(nor, to, page_size, retlen, buf); + + /* write everything in nor->page_size chunks */ + for (i = page_size; i < len; i += page_size) { + page_size = len - i; + if (page_size > nor->page_size) + page_size = nor->page_size; + + wait_till_ready(nor); + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + +write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return 0; +} + +static int macronix_quad_enable(struct spi_nor *nor) +{ + int ret, val; + + val = read_sr(nor); + write_enable(nor); + + nor->cmd_buf[0] = val | SR_QUAD_EN_MX; + nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + + if (wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct spi_nor *nor, u16 val) +{ + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + + return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 2, 0); +} + +static int spansion_quad_enable(struct spi_nor *nor) +{ + int ret; + int quad_en = CR_QUAD_EN_SPAN << 8; + + write_enable(nor); + + ret = write_sr_cr(nor, quad_en); + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + + /* read back and check it */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Macronix quad-read not enabled\n"); + return -EINVAL; + } + return status; + default: + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Spansion quad-read not enabled\n"); + return -EINVAL; + } + return status; + } +} + +static int spi_nor_check(struct spi_nor *nor) +{ + if (!nor->dev || !nor->read || !nor->write || + !nor->read_reg || !nor->write_reg || !nor->erase) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } + + if (!nor->read_id) + nor->read_id = spi_nor_read_id; + if (!nor->wait_till_ready) + nor->wait_till_ready = spi_nor_wait_till_ready; + + return 0; +} + +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode) +{ + struct flash_info *info; + struct flash_platform_data *data; + struct device *dev = nor->dev; + struct mtd_info *mtd = nor->mtd; + struct device_node *np = dev->of_node; + int ret; + int i; + + ret = spi_nor_check(nor); + if (ret) + return ret; + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. + */ + data = dev_get_platdata(dev); + if (data && data->type) { + const struct spi_device_id *plat_id; + + for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { + plat_id = &spi_nor_ids[i]; + if (strcmp(data->type, plat_id->name)) + continue; + break; + } + + if (i < ARRAY_SIZE(spi_nor_ids) - 1) + id = plat_id; + else + dev_warn(dev, "unrecognized id %s\n", data->type); + } + + info = (void *)id->driver_data; + + if (info->jedec_id) { + const struct spi_device_id *jid; + + jid = jedec_probe(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { + /* + * JEDEC knows better, so overwrite platform ID. We + * can't trust partitions any longer, but we'll let + * mtd apply them anyway, since some partitions may be + * marked read-only, and we don't want to lose that + * information, even if it's not 100% accurate. + */ + dev_warn(dev, "found %s, expected %s\n", + jid->name, id->name); + id = jid; + info = (void *)jid->driver_data; + } + } + + mutex_init(&nor->lock); + + /* + * Atmel, SST and Intel/Numonyx serial nor tend to power + * up with the software protection bits set + */ + + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { + write_enable(nor); + write_sr(nor, 0); + } + + if (data && data->name) + mtd->name = data->name; + else + mtd->name = dev_name(dev); + + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + mtd->size = info->sector_size * info->n_sectors; + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + + /* nor protection support for STmicro chips */ + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + } + + /* sst nor chips use AAI word program */ + if (info->flags & SST_WRITE) + mtd->_write = sst_write; + else + mtd->_write = spi_nor_write; + + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = OPCODE_BE_4K; + mtd->erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + nor->erase_opcode = OPCODE_BE_4K_PMC; + mtd->erasesize = 4096; + } else { + nor->erase_opcode = OPCODE_SE; + mtd->erasesize = info->sector_size; + } + + if (info->flags & SPI_NOR_NO_ERASE) + mtd->flags |= MTD_NO_ERASE; + + mtd->dev.parent = dev; + nor->page_size = info->page_size; + mtd->writebufsize = nor->page_size; + + if (np) { + /* If we were instantiated by DT, use it */ + if (of_property_read_bool(np, "m25p,fast-read")) + nor->flash_read = SPI_NOR_FAST; + else + nor->flash_read = SPI_NOR_NORMAL; + } else { + /* If we weren't instantiated by DT, default to fast-read */ + nor->flash_read = SPI_NOR_FAST; + } + + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; + + /* Quad/Dual-read mode takes precedence over fast/normal */ + if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + ret = set_quad_mode(nor, info->jedec_id); + if (ret) { + dev_err(dev, "quad mode not supported\n"); + return ret; + } + nor->flash_read = SPI_NOR_QUAD; + } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { + nor->flash_read = SPI_NOR_DUAL; + } + + /* Default commands */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: + nor->read_opcode = OPCODE_QUAD_READ; + break; + case SPI_NOR_DUAL: + nor->read_opcode = OPCODE_DUAL_READ; + break; + case SPI_NOR_FAST: + nor->read_opcode = OPCODE_FAST_READ; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = OPCODE_NORM_READ; + break; + default: + dev_err(dev, "No Read opcode defined\n"); + return -EINVAL; + } + + nor->program_opcode = OPCODE_PP; + + if (info->addr_width) + nor->addr_width = info->addr_width; + else if (mtd->size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: + nor->read_opcode = OPCODE_QUAD_READ_4B; + break; + case SPI_NOR_DUAL: + nor->read_opcode = OPCODE_DUAL_READ_4B; + break; + case SPI_NOR_FAST: + nor->read_opcode = OPCODE_FAST_READ_4B; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = OPCODE_NORM_READ_4B; + break; + } + nor->program_opcode = OPCODE_PP_4B; + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = OPCODE_SE_4B; + mtd->erasesize = info->sector_size; + } else + set_4byte(nor, info->jedec_id, 1); + } else { + nor->addr_width = 3; + } + + nor->read_dummy = spi_nor_read_dummy_cycles(nor); + + dev_info(dev, "%s (%lld Kbytes)\n", id->name, + (long long)mtd->size >> 10); + + dev_dbg(dev, + "mtd .name = %s, .size = 0x%llx (%lldMiB), " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20), + mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions); + + if (mtd->numeraseregions) + for (i = 0; i < mtd->numeraseregions; i++) + dev_dbg(dev, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].erasesize / 1024, + mtd->eraseregions[i].numblocks); + return 0; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huang Shijie "); +MODULE_AUTHOR("Mike Lavender"); +MODULE_DESCRIPTION("framework for SPI NOR"); -- cgit v1.2.3 From 03e296f613affcc2671c1e86d8c25ecad867204e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 20 Mar 2014 05:00:12 -0700 Subject: mtd: m25p80: use the SPI nor framework Use the new SPI nor framework, and rewrite the m25p80: (0) remove all the NOR comands. (1) change the m25p->command to an array. (2) implement the necessary hooks, such as m25p80_read/m25p80_write. Tested with the m25p32. Signed-off-by: Huang Shijie Acked-by: Marek Vasut [Brian: rebased] Signed-off-by: Brian Norris --- drivers/mtd/devices/Kconfig | 2 +- drivers/mtd/devices/m25p80.c | 1303 ++++-------------------------------------- 2 files changed, 106 insertions(+), 1199 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 1210bc2923b7..48aa1aa5820b 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP config MTD_M25P80 tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" - depends on SPI_MASTER + depends on SPI_MASTER && MTD_SPI_NOR_BASE help This enables access to most modern SPI flash chips, used for program and data storage. Series supported include Atmel AT26DF, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 524dab3ac938..4af6400ccd95 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -19,485 +19,98 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include -#include #include #include +#include -/* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ -#define OPCODE_RDCR 0x35 /* Read configuration register */ - -/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ - -/* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ - -/* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ - -/* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ - -/* Status Register bits. */ -#define SR_WIP 1 /* Write in progress */ -#define SR_WEL 2 /* Write enable latch */ -/* meaning of other SR_* bits may differ between vendors */ -#define SR_BP0 4 /* Block protect 0 */ -#define SR_BP1 8 /* Block protect 1 */ -#define SR_BP2 0x10 /* Block protect 2 */ -#define SR_SRWD 0x80 /* SR write protect */ - -#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ - -/* Configuration Register bits. */ -#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ - -/* Define max times to check status register before we give up. */ -#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 6 - -#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) - -/****************************************************************************/ - -enum read_type { - M25P80_NORMAL = 0, - M25P80_FAST, - M25P80_DUAL, - M25P80_QUAD, -}; - struct m25p { struct spi_device *spi; - struct mutex lock; + struct spi_nor spi_nor; struct mtd_info mtd; - u16 page_size; - u16 addr_width; - u8 erase_opcode; - u8 read_opcode; - u8 program_opcode; - u8 *command; - enum read_type flash_read; + u8 command[MAX_CMD_SIZE]; }; -static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) -{ - return container_of(mtd, struct m25p, mtd); -} - -/****************************************************************************/ - -/* - * Internal helper functions - */ - -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. - */ -static int read_sr(struct m25p *flash) -{ - ssize_t retval; - u8 code = OPCODE_RDSR; - u8 val; - - retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); - - if (retval < 0) { - dev_err(&flash->spi->dev, "error %d reading SR\n", - (int) retval); - return retval; - } - - return val; -} - -/* - * Read configuration register, returning its value in the - * location. Return the configuration register value. - * Returns negative if error occured. - */ -static int read_cr(struct m25p *flash) -{ - u8 code = OPCODE_RDCR; - int ret; - u8 val; - - ret = spi_write_then_read(flash->spi, &code, 1, &val, 1); - if (ret < 0) { - dev_err(&flash->spi->dev, "error %d reading CR\n", ret); - return ret; - } - - return val; -} - -/* - * Write status register 1 byte - * Returns negative if error occurred. - */ -static int write_sr(struct m25p *flash, u8 val) -{ - flash->command[0] = OPCODE_WRSR; - flash->command[1] = val; - - return spi_write(flash->spi, flash->command, 2); -} - -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static inline int write_enable(struct m25p *flash) -{ - u8 code = OPCODE_WREN; - - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Send write disble instruction to the chip. - */ -static inline int write_disable(struct m25p *flash) -{ - u8 code = OPCODE_WRDI; - - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Enable/disable 4-byte addressing mode. - */ -static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) -{ - int status; - bool need_wren = false; - - switch (JEDEC_MFR(jedec_id)) { - case CFI_MFR_ST: /* Micron, actually */ - /* Some Micron need WREN command; all will accept it */ - need_wren = true; - case CFI_MFR_MACRONIX: - case 0xEF /* winbond */: - if (need_wren) - write_enable(flash); - - flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; - status = spi_write(flash->spi, flash->command, 1); - - if (need_wren) - write_disable(flash); - - return status; - default: - /* Spansion style */ - flash->command[0] = OPCODE_BRWR; - flash->command[1] = enable << 7; - return spi_write(flash->spi, flash->command, 2); - } -} - -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. - */ -static int wait_till_ready(struct m25p *flash) -{ - unsigned long deadline; - int sr; - - deadline = jiffies + MAX_READY_WAIT_JIFFIES; - - do { - if ((sr = read_sr(flash)) < 0) - break; - else if (!(sr & SR_WIP)) - return 0; - - cond_resched(); - - } while (!time_after_eq(jiffies, deadline)); - - return 1; -} - -/* - * Write status Register and configuration register with 2 bytes - * The first byte will be written to the status register, while the - * second byte will be written to the configuration register. - * Return negative if error occured. - */ -static int write_sr_cr(struct m25p *flash, u16 val) -{ - flash->command[0] = OPCODE_WRSR; - flash->command[1] = val & 0xff; - flash->command[2] = (val >> 8); - - return spi_write(flash->spi, flash->command, 3); -} - -static int macronix_quad_enable(struct m25p *flash) -{ - int ret, val; - u8 cmd[2]; - cmd[0] = OPCODE_WRSR; - - val = read_sr(flash); - cmd[1] = val | SR_QUAD_EN_MX; - write_enable(flash); - - spi_write(flash->spi, &cmd, 2); - - if (wait_till_ready(flash)) - return 1; - - ret = read_sr(flash); - if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { - dev_err(&flash->spi->dev, "Macronix Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -static int spansion_quad_enable(struct m25p *flash) +static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) { + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; int ret; - int quad_en = CR_QUAD_EN_SPAN << 8; - - write_enable(flash); - ret = write_sr_cr(flash, quad_en); - if (ret < 0) { - dev_err(&flash->spi->dev, - "error while writing configuration register\n"); - return -EINVAL; - } - - /* read back and check it */ - ret = read_cr(flash); - if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { - dev_err(&flash->spi->dev, "Spansion Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -static int set_quad_mode(struct m25p *flash, u32 jedec_id) -{ - int status; - - switch (JEDEC_MFR(jedec_id)) { - case CFI_MFR_MACRONIX: - status = macronix_quad_enable(flash); - if (status) { - dev_err(&flash->spi->dev, - "Macronix quad-read not enabled\n"); - return -EINVAL; - } - return status; - default: - status = spansion_quad_enable(flash); - if (status) { - dev_err(&flash->spi->dev, - "Spansion quad-read not enabled\n"); - return -EINVAL; - } - return status; - } -} - -/* - * Erase the whole flash memory - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_chip(struct m25p *flash) -{ - pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__, - (long long)(flash->mtd.size >> 10)); + ret = spi_write_then_read(spi, &code, 1, val, len); + if (ret < 0) + dev_err(&spi->dev, "error %d reading %x\n", ret, code); - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) - return 1; - - /* Send write enable, then erase commands. */ - write_enable(flash); - - /* Set up command buffer. */ - flash->command[0] = OPCODE_CHIP_ERASE; - - spi_write(flash->spi, flash->command, 1); - - return 0; + return ret; } -static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) { /* opcode is in cmd[0] */ - cmd[1] = addr >> (flash->addr_width * 8 - 8); - cmd[2] = addr >> (flash->addr_width * 8 - 16); - cmd[3] = addr >> (flash->addr_width * 8 - 24); - cmd[4] = addr >> (flash->addr_width * 8 - 32); + cmd[1] = addr >> (nor->addr_width * 8 - 8); + cmd[2] = addr >> (nor->addr_width * 8 - 16); + cmd[3] = addr >> (nor->addr_width * 8 - 24); + cmd[4] = addr >> (nor->addr_width * 8 - 32); } -static int m25p_cmdsz(struct m25p *flash) +static int m25p_cmdsz(struct spi_nor *nor) { - return 1 + flash->addr_width; + return 1 + nor->addr_width; } -/* - * Erase one sector of flash memory at offset ``offset'' which is any - * address within the sector which should be erased. - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_sector(struct m25p *flash, u32 offset) +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int wr_en) { - pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev), - __func__, flash->mtd.erasesize / 1024, offset); - - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) - return 1; + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; - /* Send write enable, then erase commands. */ - write_enable(flash); - - /* Set up command buffer. */ - flash->command[0] = flash->erase_opcode; - m25p_addr2cmd(flash, offset, flash->command); - - spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); + flash->command[0] = opcode; + if (buf) + memcpy(&flash->command[1], buf, len); - return 0; + return spi_write(spi, flash->command, len + 1); } -/****************************************************************************/ - -/* - * MTD implementation - */ - -/* - * Erase an address range on the flash chip. The address range may extend - * one or more erase sectors. Return an error is there is a problem erasing. - */ -static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) +static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - struct m25p *flash = mtd_to_m25p(mtd); - u32 addr,len; - uint32_t rem; - - pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev), - __func__, (long long)instr->addr, - (long long)instr->len); - - div_u64_rem(instr->len, mtd->erasesize, &rem); - if (rem) - return -EINVAL; - - addr = instr->addr; - len = instr->len; - - mutex_lock(&flash->lock); - - /* whole-chip erase? */ - if (len == flash->mtd.size) { - if (erase_chip(flash)) { - instr->state = MTD_ERASE_FAILED; - mutex_unlock(&flash->lock); - return -EIO; - } + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; + struct spi_transfer t[2] = {}; + struct spi_message m; + int cmd_sz = m25p_cmdsz(nor); - /* REVISIT in some cases we could speed up erasing large regions - * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up - * to use "small sector erase", but that's not always optimal. - */ + spi_message_init(&m); - /* "sector"-at-a-time erase */ - } else { - while (len) { - if (erase_sector(flash, addr)) { - instr->state = MTD_ERASE_FAILED; - mutex_unlock(&flash->lock); - return -EIO; - } + if (nor->program_opcode == OPCODE_AAI_WP && nor->sst_write_second) + cmd_sz = 1; - addr += mtd->erasesize; - len -= mtd->erasesize; - } - } + flash->command[0] = nor->program_opcode; + m25p_addr2cmd(nor, to, flash->command); - mutex_unlock(&flash->lock); + t[0].tx_buf = flash->command; + t[0].len = cmd_sz; + spi_message_add_tail(&t[0], &m); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + t[1].tx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); - return 0; -} + spi_sync(spi, &m); -/* - * Dummy Cycle calculation for different type of read. - * It can be used to support more commands with - * different dummy cycle requirements. - */ -static inline int m25p80_dummy_cycles_read(struct m25p *flash) -{ - switch (flash->flash_read) { - case M25P80_FAST: - case M25P80_DUAL: - case M25P80_QUAD: - return 1; - case M25P80_NORMAL: - return 0; - default: - dev_err(&flash->spi->dev, "No valid read type supported\n"); - return -1; - } + *retlen += m.actual_length - cmd_sz; } -static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) +static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) { - switch (flash->flash_read) { - case M25P80_DUAL: + switch (nor->flash_read) { + case SPI_NOR_DUAL: return 2; - case M25P80_QUAD: + case SPI_NOR_QUAD: return 4; default: return 0; @@ -505,590 +118,72 @@ static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) } /* - * Read an address range from the flash chip. The address range + * Read an address range from the nor chip. The address range * may be any size provided it is within the physical boundaries. */ -static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) { - struct m25p *flash = mtd_to_m25p(mtd); + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; struct spi_transfer t[2]; struct spi_message m; - uint8_t opcode; - int dummy; + int dummy = nor->read_dummy; + int ret; - pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)from, len); + /* Wait till previous write/erase is done. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; spi_message_init(&m); memset(t, 0, (sizeof t)); - dummy = m25p80_dummy_cycles_read(flash); - if (dummy < 0) { - dev_err(&flash->spi->dev, "No valid read command supported\n"); - return -EINVAL; - } + flash->command[0] = nor->read_opcode; + m25p_addr2cmd(nor, from, flash->command); t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + dummy; + t[0].len = m25p_cmdsz(nor) + dummy; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; - t[1].rx_nbits = m25p80_rx_nbits(flash); + t[1].rx_nbits = m25p80_rx_nbits(nor); t[1].len = len; spi_message_add_tail(&t[1], &m); - mutex_lock(&flash->lock); - - /* Wait till previous write/erase is done. */ - if (wait_till_ready(flash)) { - /* REVISIT status return?? */ - mutex_unlock(&flash->lock); - return 1; - } - - /* Set up the write data buffer. */ - opcode = flash->read_opcode; - flash->command[0] = opcode; - m25p_addr2cmd(flash, from, flash->command); - - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash) - dummy; - - mutex_unlock(&flash->lock); + spi_sync(spi, &m); + *retlen = m.actual_length - m25p_cmdsz(nor) - dummy; return 0; } -/* - * Write an address range to the flash chip. Data must be written in - * FLASH_PAGESIZE chunks. The address range may be any size provided - * it is within the physical boundaries. - */ -static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int m25p80_erase(struct spi_nor *nor, loff_t offset) { - struct m25p *flash = mtd_to_m25p(mtd); - u32 page_offset, page_size; - struct spi_transfer t[2]; - struct spi_message m; - - pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)to, len); - - spi_message_init(&m); - memset(t, 0, (sizeof t)); - - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - spi_message_add_tail(&t[1], &m); - - mutex_lock(&flash->lock); - - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) { - mutex_unlock(&flash->lock); - return 1; - } - - write_enable(flash); - - /* Set up the opcode in the write buffer. */ - flash->command[0] = flash->program_opcode; - m25p_addr2cmd(flash, to, flash->command); - - page_offset = to & (flash->page_size - 1); - - /* do all the bytes fit onto one page? */ - if (page_offset + len <= flash->page_size) { - t[1].len = len; - - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash); - } else { - u32 i; - - /* the size of data remaining on the first page */ - page_size = flash->page_size - page_offset; - - t[1].len = page_size; - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash); - - /* write everything in flash->page_size chunks */ - for (i = page_size; i < len; i += page_size) { - page_size = len - i; - if (page_size > flash->page_size) - page_size = flash->page_size; - - /* write the next page to flash */ - m25p_addr2cmd(flash, to + i, flash->command); - - t[1].tx_buf = buf + i; - t[1].len = page_size; - - wait_till_ready(flash); - - write_enable(flash); - - spi_sync(flash->spi, &m); - - *retlen += m.actual_length - m25p_cmdsz(flash); - } - } - - mutex_unlock(&flash->lock); - - return 0; -} - -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct m25p *flash = mtd_to_m25p(mtd); - struct spi_transfer t[2]; - struct spi_message m; - size_t actual; - int cmd_sz, ret; - - pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)to, len); - - spi_message_init(&m); - memset(t, 0, (sizeof t)); - - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - spi_message_add_tail(&t[1], &m); + struct m25p *flash = nor->priv; + int ret; - mutex_lock(&flash->lock); + dev_dbg(nor->dev, "%dKiB at 0x%08x\n", + flash->mtd.erasesize / 1024, (u32)offset); /* Wait until finished previous write command. */ - ret = wait_till_ready(flash); + ret = nor->wait_till_ready(nor); if (ret) - goto time_out; - - write_enable(flash); - - actual = to % 2; - /* Start write from odd address. */ - if (actual) { - flash->command[0] = OPCODE_BP; - m25p_addr2cmd(flash, to, flash->command); - - /* write one byte. */ - t[1].len = 1; - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - m25p_cmdsz(flash); - } - to += actual; - - flash->command[0] = OPCODE_AAI_WP; - m25p_addr2cmd(flash, to, flash->command); - - /* Write out most of the data here. */ - cmd_sz = m25p_cmdsz(flash); - for (; actual < len - 1; actual += 2) { - t[0].len = cmd_sz; - /* write two bytes. */ - t[1].len = 2; - t[1].tx_buf = buf + actual; + return ret; - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - cmd_sz; - cmd_sz = 1; - to += 2; - } - write_disable(flash); - ret = wait_till_ready(flash); + /* Send write enable, then erase commands. */ + ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); if (ret) - goto time_out; - - /* Write out trailing byte if it exists. */ - if (actual != len) { - write_enable(flash); - flash->command[0] = OPCODE_BP; - m25p_addr2cmd(flash, to, flash->command); - t[0].len = m25p_cmdsz(flash); - t[1].len = 1; - t[1].tx_buf = buf + actual; - - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - m25p_cmdsz(flash); - write_disable(flash); - } - -time_out: - mutex_unlock(&flash->lock); - return ret; -} - -static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct m25p *flash = mtd_to_m25p(mtd); - uint32_t offset = ofs; - uint8_t status_old, status_new; - int res = 0; - - mutex_lock(&flash->lock); - /* Wait until finished previous command */ - if (wait_till_ready(flash)) { - res = 1; - goto err; - } - - status_old = read_sr(flash); - - if (offset < flash->mtd.size-(flash->mtd.size/2)) - status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/4)) - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - else if (offset < flash->mtd.size-(flash->mtd.size/8)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/16)) - status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; - else if (offset < flash->mtd.size-(flash->mtd.size/32)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/64)) - status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; - else - status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - - /* Only modify protection if it will not unlock other areas */ - if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) > - (status_old&(SR_BP2|SR_BP1|SR_BP0))) { - write_enable(flash); - if (write_sr(flash, status_new) < 0) { - res = 1; - goto err; - } - } - -err: mutex_unlock(&flash->lock); - return res; -} - -static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct m25p *flash = mtd_to_m25p(mtd); - uint32_t offset = ofs; - uint8_t status_old, status_new; - int res = 0; - - mutex_lock(&flash->lock); - /* Wait until finished previous command */ - if (wait_till_ready(flash)) { - res = 1; - goto err; - } - - status_old = read_sr(flash); - - if (offset+len > flash->mtd.size-(flash->mtd.size/64)) - status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0); - else if (offset+len > flash->mtd.size-(flash->mtd.size/32)) - status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - else if (offset+len > flash->mtd.size-(flash->mtd.size/16)) - status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; - else if (offset+len > flash->mtd.size-(flash->mtd.size/8)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset+len > flash->mtd.size-(flash->mtd.size/4)) - status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; - else if (offset+len > flash->mtd.size-(flash->mtd.size/2)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - - /* Only modify protection if it will not lock other areas */ - if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) < - (status_old&(SR_BP2|SR_BP1|SR_BP0))) { - write_enable(flash); - if (write_sr(flash, status_new) < 0) { - res = 1; - goto err; - } - } - -err: mutex_unlock(&flash->lock); - return res; -} - -/****************************************************************************/ - -/* - * SPI device driver setup and teardown - */ - -struct flash_info { - /* JEDEC id zero means "no ID" (most older chips); otherwise it has - * a high byte of zero plus three data bytes: the manufacturer id, - * then a two byte device id. - */ - u32 jedec_id; - u16 ext_id; - - /* The size listed here is what works with OPCODE_SE, which isn't - * necessarily called a "sector" by the vendor. - */ - unsigned sector_size; - u16 n_sectors; - - u16 page_size; - u16 addr_width; - - u16 flags; -#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ -#define M25P_NO_ERASE 0x02 /* No erase command needed */ -#define SST_WRITE 0x04 /* use SST byte programming */ -#define M25P_NO_FR 0x08 /* Can't do fastread */ -#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ -#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */ -#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */ -}; - -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ - .jedec_id = (_jedec_id), \ - .ext_id = (_ext_id), \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), \ - }) - -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = (_page_size), \ - .addr_width = (_addr_width), \ - .flags = (_flags), \ - }) - -/* NOTE: double check command sets and memory organization when you add - * more flash chips. This current list focusses on newer chips, which - * have been converging on command sets which including JEDEC ID. - */ -static const struct spi_device_id m25p_ids[] = { - /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, - - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - - { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - - { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, - - /* EON -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, - { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, - { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, - { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, - - /* ESMT */ - { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, - - /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, - - /* GigaDevice */ - { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, - { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, - - /* Intel/Numonyx -- xxxs33b */ - { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, - { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, - { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, - - /* Macronix */ - { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, - { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, - { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, - { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, - { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, - { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, - { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, - { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, - { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, - { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, - { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) }, - { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) }, - - /* Micron */ - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, - { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, - - /* PMC */ - { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, - { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, - { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, - - /* Spansion -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, - { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, - { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) }, - { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, - { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, - { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, - { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, - { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, - { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, - { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, - { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, - { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, - { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, - { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, - { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - - /* SST -- large erase sizes are "overlays", "sectors" are 4K */ - { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, - { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, - { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, - { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, - { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, - { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, - { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, - { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - - /* ST Microelectronics -- newer production may have feature updates */ - { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, - { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, - { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, - { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, - { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, - { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, - { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, - { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, - { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, - { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, - - { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, - { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, - { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, - { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, - { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, - { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, - { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, - { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, - { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, - - { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, - { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, - { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, - - { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, - { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, - { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, - - { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, - { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, - - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ - { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, - { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, - { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, - { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, - - /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { }, -}; -MODULE_DEVICE_TABLE(spi, m25p_ids); - -static const struct spi_device_id *jedec_probe(struct spi_device *spi) -{ - int tmp; - u8 code = OPCODE_RDID; - u8 id[5]; - u32 jedec; - u16 ext_jedec; - struct flash_info *info; + return ret; - /* JEDEC also defines an optional "extended device information" - * string for after vendor-specific data, after the three bytes - * we use here. Supporting some chips might require using it. - */ - tmp = spi_write_then_read(spi, &code, 1, id, 5); - if (tmp < 0) { - pr_debug("%s: error %d reading JEDEC ID\n", - dev_name(&spi->dev), tmp); - return ERR_PTR(tmp); - } - jedec = id[0]; - jedec = jedec << 8; - jedec |= id[1]; - jedec = jedec << 8; - jedec |= id[2]; + /* Set up command buffer. */ + flash->command[0] = nor->erase_opcode; + m25p_addr2cmd(nor, offset, flash->command); - ext_jedec = id[3] << 8 | id[4]; + spi_write(flash->spi, flash->command, m25p_cmdsz(nor)); - for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { - info = (void *)m25p_ids[tmp].driver_data; - if (info->jedec_id == jedec) { - if (info->ext_id == 0 || info->ext_id == ext_jedec) - return &m25p_ids[tmp]; - } - } - dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); - return ERR_PTR(-ENODEV); + return 0; } - /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -1096,231 +191,43 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi) */ static int m25p_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); - struct flash_platform_data *data; - struct m25p *flash; - struct flash_info *info; - unsigned i; struct mtd_part_parser_data ppdata; - struct device_node *np = spi->dev.of_node; + struct flash_platform_data *data; + struct m25p *flash; + struct spi_nor *nor; + enum read_mode mode = SPI_NOR_NORMAL; int ret; - /* Platform data helps sort out which chip type we have, as - * well as how this board partitions it. If we don't have - * a chip ID, try the JEDEC id commands; they'll work for most - * newer chips, even if we don't recognize the particular chip. - */ - data = dev_get_platdata(&spi->dev); - if (data && data->type) { - const struct spi_device_id *plat_id; - - for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { - plat_id = &m25p_ids[i]; - if (strcmp(data->type, plat_id->name)) - continue; - break; - } - - if (i < ARRAY_SIZE(m25p_ids) - 1) - id = plat_id; - else - dev_warn(&spi->dev, "unrecognized id %s\n", data->type); - } - - info = (void *)id->driver_data; - - if (info->jedec_id) { - const struct spi_device_id *jid; - - jid = jedec_probe(spi); - if (IS_ERR(jid)) { - return PTR_ERR(jid); - } else if (jid != id) { - /* - * JEDEC knows better, so overwrite platform ID. We - * can't trust partitions any longer, but we'll let - * mtd apply them anyway, since some partitions may be - * marked read-only, and we don't want to lose that - * information, even if it's not 100% accurate. - */ - dev_warn(&spi->dev, "found %s, expected %s\n", - jid->name, id->name); - id = jid; - info = (void *)jid->driver_data; - } - } - flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL); - if (!flash->command) - return -ENOMEM; - - flash->spi = spi; - mutex_init(&flash->lock); - spi_set_drvdata(spi, flash); - - /* - * Atmel, SST and Intel/Numonyx serial flash tend to power - * up with the software protection bits set - */ + nor = &flash->spi_nor; - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || - JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || - JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { - write_enable(flash); - write_sr(flash, 0); - } - - if (data && data->name) - flash->mtd.name = data->name; - else - flash->mtd.name = dev_name(&spi->dev); - - flash->mtd.type = MTD_NORFLASH; - flash->mtd.writesize = 1; - flash->mtd.flags = MTD_CAP_NORFLASH; - flash->mtd.size = info->sector_size * info->n_sectors; - flash->mtd._erase = m25p80_erase; - flash->mtd._read = m25p80_read; - - /* flash protection support for STmicro chips */ - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { - flash->mtd._lock = m25p80_lock; - flash->mtd._unlock = m25p80_unlock; - } + /* install the hooks */ + nor->read = m25p80_read; + nor->write = m25p80_write; + nor->erase = m25p80_erase; + nor->write_reg = m25p80_write_reg; + nor->read_reg = m25p80_read_reg; - /* sst flash chips use AAI word program */ - if (info->flags & SST_WRITE) - flash->mtd._write = sst_write; - else - flash->mtd._write = m25p80_write; + nor->dev = &spi->dev; + nor->mtd = &flash->mtd; + nor->priv = flash; - /* prefer "small sector" erase if possible */ - if (info->flags & SECT_4K) { - flash->erase_opcode = OPCODE_BE_4K; - flash->mtd.erasesize = 4096; - } else if (info->flags & SECT_4K_PMC) { - flash->erase_opcode = OPCODE_BE_4K_PMC; - flash->mtd.erasesize = 4096; - } else { - flash->erase_opcode = OPCODE_SE; - flash->mtd.erasesize = info->sector_size; - } + spi_set_drvdata(spi, flash); + flash->mtd.priv = nor; + flash->spi = spi; - if (info->flags & M25P_NO_ERASE) - flash->mtd.flags |= MTD_NO_ERASE; + if (spi->mode & SPI_RX_QUAD) + mode = SPI_NOR_QUAD; + ret = spi_nor_scan(nor, spi_get_device_id(spi), mode); + if (ret) + return ret; + data = dev_get_platdata(&spi->dev); ppdata.of_node = spi->dev.of_node; - flash->mtd.dev.parent = &spi->dev; - flash->page_size = info->page_size; - flash->mtd.writebufsize = flash->page_size; - - if (np) { - /* If we were instantiated by DT, use it */ - if (of_property_read_bool(np, "m25p,fast-read")) - flash->flash_read = M25P80_FAST; - else - flash->flash_read = M25P80_NORMAL; - } else { - /* If we weren't instantiated by DT, default to fast-read */ - flash->flash_read = M25P80_FAST; - } - - /* Some devices cannot do fast-read, no matter what DT tells us */ - if (info->flags & M25P_NO_FR) - flash->flash_read = M25P80_NORMAL; - - /* Quad/Dual-read mode takes precedence over fast/normal */ - if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) { - ret = set_quad_mode(flash, info->jedec_id); - if (ret) { - dev_err(&flash->spi->dev, "quad mode not supported\n"); - return ret; - } - flash->flash_read = M25P80_QUAD; - } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) { - flash->flash_read = M25P80_DUAL; - } - /* Default commands */ - switch (flash->flash_read) { - case M25P80_QUAD: - flash->read_opcode = OPCODE_QUAD_READ; - break; - case M25P80_DUAL: - flash->read_opcode = OPCODE_DUAL_READ; - break; - case M25P80_FAST: - flash->read_opcode = OPCODE_FAST_READ; - break; - case M25P80_NORMAL: - flash->read_opcode = OPCODE_NORM_READ; - break; - default: - dev_err(&flash->spi->dev, "No Read opcode defined\n"); - return -EINVAL; - } - - flash->program_opcode = OPCODE_PP; - - if (info->addr_width) - flash->addr_width = info->addr_width; - else if (flash->mtd.size > 0x1000000) { - /* enable 4-byte addressing if the device exceeds 16MiB */ - flash->addr_width = 4; - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { - /* Dedicated 4-byte command set */ - switch (flash->flash_read) { - case M25P80_QUAD: - flash->read_opcode = OPCODE_QUAD_READ_4B; - break; - case M25P80_DUAL: - flash->read_opcode = OPCODE_DUAL_READ_4B; - break; - case M25P80_FAST: - flash->read_opcode = OPCODE_FAST_READ_4B; - break; - case M25P80_NORMAL: - flash->read_opcode = OPCODE_NORM_READ_4B; - break; - } - flash->program_opcode = OPCODE_PP_4B; - /* No small sector erase for 4-byte command set */ - flash->erase_opcode = OPCODE_SE_4B; - flash->mtd.erasesize = info->sector_size; - } else - set_4byte(flash, info->jedec_id, 1); - } else { - flash->addr_width = 3; - } - - dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, - (long long)flash->mtd.size >> 10); - - pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) " - ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", - flash->mtd.name, - (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), - flash->mtd.erasesize, flash->mtd.erasesize / 1024, - flash->mtd.numeraseregions); - - if (flash->mtd.numeraseregions) - for (i = 0; i < flash->mtd.numeraseregions; i++) - pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, " - ".erasesize = 0x%.8x (%uKiB), " - ".numblocks = %d }\n", - i, (long long)flash->mtd.eraseregions[i].offset, - flash->mtd.eraseregions[i].erasesize, - flash->mtd.eraseregions[i].erasesize / 1024, - flash->mtd.eraseregions[i].numblocks); - - - /* partitions should match sector boundaries; and it may be good to - * use readonly partitions for writeprotected sectors (BP2..BP0). - */ return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, data ? data->parts : NULL, data ? data->nr_parts : 0); @@ -1341,7 +248,7 @@ static struct spi_driver m25p80_driver = { .name = "m25p80", .owner = THIS_MODULE, }, - .id_table = m25p_ids, + .id_table = spi_nor_ids, .probe = m25p_probe, .remove = m25p_remove, -- cgit v1.2.3 From 0d8c11c01274bde227d368daa8954911dd324a9f Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:40 +0800 Subject: mtd: spi-nor: add a helper to find the spi_device_id Add the spi_nor_match_id() to find the proper spi_device_id with the NOR flash's name in the spi_nor_ids table. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 50b929095bdb..f7c9e638623b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1082,6 +1082,18 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, return 0; } +const struct spi_device_id *spi_nor_match_id(char *name) +{ + const struct spi_device_id *id = spi_nor_ids; + + while (id->name[0]) { + if (!strcmp(name, id->name)) + return id; + id++; + } + return NULL; +} + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huang Shijie "); MODULE_AUTHOR("Mike Lavender"); -- cgit v1.2.3 From e46ecda764dc37f9fc6279d95ea2c007daef1a71 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:42 +0800 Subject: mtd: spi-nor: Add Freescale QuadSPI driver (0) What is the QuadSPI controller? The QuadSPI(Quad Serial Peripheral Interface) acts as an interface to one single or two external serial flash devices, each with up to 4 bidirectional data lines. (1) The QuadSPI controller is driven by the LUT(Look-up Table) registers. The LUT registers are a look-up-table for sequences of instructions. A valid sequence consists of four LUT registers. (2) The definition of the LUT register shows below: --------------------------------------------------- | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | --------------------------------------------------- There are several types of INSTRx, such as: CMD : the SPI NOR command. ADDR : the address for the SPI NOR command. DUMMY : the dummy cycles needed by the SPI NOR command. .... There are several types of PADx, such as: PAD1 : use a singe I/O line. PAD2 : use two I/O lines. PAD4 : use quad I/O lines. .... (3) Test this driver with the JFFS2 and UBIFS: For jffs2: ------------- #flash_eraseall /dev/mtd0 #mount -t jffs2 /dev/mtdblock0 tmp #bonnie++ -d tmp -u 0 -s 10 -r 5 For ubifs: ------------- #flash_eraseall /dev/mtd0 #ubiattach /dev/ubi_ctrl -m 0 #ubimkvol /dev/ubi0 -N test -m #mount -t ubifs ubi0:test tmp #bonnie++ -d tmp -u 0 -s 10 -r 5 Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/Kconfig | 6 + drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/fsl-quadspi.c | 1009 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1016 insertions(+) create mode 100644 drivers/mtd/spi-nor/fsl-quadspi.c (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 41591af67aca..64cfc39ad17e 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -4,3 +4,9 @@ config MTD_SPI_NOR_BASE help This is the framework for the SPI NOR which can be used by the SPI device drivers and the SPI-NOR device driver. +config SPI_FSL_QUADSPI + tristate "Freescale Quad SPI controller" + depends on ARCH_MXC && MTD_SPI_NOR_BASE + help + This enables support for the Quad SPI controller in master mode. + We only connect the NOR to this controller now. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 7dfe1f9b6940..51f9d8b99ab7 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o +obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c new file mode 100644 index 000000000000..6dc08ed950c8 --- /dev/null +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -0,0 +1,1009 @@ +/* + * Freescale QuadSPI driver. + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The registers */ +#define QUADSPI_MCR 0x00 +#define QUADSPI_MCR_RESERVED_SHIFT 16 +#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT) +#define QUADSPI_MCR_MDIS_SHIFT 14 +#define QUADSPI_MCR_MDIS_MASK (1 << QUADSPI_MCR_MDIS_SHIFT) +#define QUADSPI_MCR_CLR_TXF_SHIFT 11 +#define QUADSPI_MCR_CLR_TXF_MASK (1 << QUADSPI_MCR_CLR_TXF_SHIFT) +#define QUADSPI_MCR_CLR_RXF_SHIFT 10 +#define QUADSPI_MCR_CLR_RXF_MASK (1 << QUADSPI_MCR_CLR_RXF_SHIFT) +#define QUADSPI_MCR_DDR_EN_SHIFT 7 +#define QUADSPI_MCR_DDR_EN_MASK (1 << QUADSPI_MCR_DDR_EN_SHIFT) +#define QUADSPI_MCR_END_CFG_SHIFT 2 +#define QUADSPI_MCR_END_CFG_MASK (3 << QUADSPI_MCR_END_CFG_SHIFT) +#define QUADSPI_MCR_SWRSTHD_SHIFT 1 +#define QUADSPI_MCR_SWRSTHD_MASK (1 << QUADSPI_MCR_SWRSTHD_SHIFT) +#define QUADSPI_MCR_SWRSTSD_SHIFT 0 +#define QUADSPI_MCR_SWRSTSD_MASK (1 << QUADSPI_MCR_SWRSTSD_SHIFT) + +#define QUADSPI_IPCR 0x08 +#define QUADSPI_IPCR_SEQID_SHIFT 24 +#define QUADSPI_IPCR_SEQID_MASK (0xF << QUADSPI_IPCR_SEQID_SHIFT) + +#define QUADSPI_BUF0CR 0x10 +#define QUADSPI_BUF1CR 0x14 +#define QUADSPI_BUF2CR 0x18 +#define QUADSPI_BUFXCR_INVALID_MSTRID 0xe + +#define QUADSPI_BUF3CR 0x1c +#define QUADSPI_BUF3CR_ALLMST_SHIFT 31 +#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) + +#define QUADSPI_BFGENCR 0x20 +#define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 +#define QUADSPI_BFGENCR_PAR_EN_MASK (1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT)) +#define QUADSPI_BFGENCR_SEQID_SHIFT 12 +#define QUADSPI_BFGENCR_SEQID_MASK (0xF << QUADSPI_BFGENCR_SEQID_SHIFT) + +#define QUADSPI_BUF0IND 0x30 +#define QUADSPI_BUF1IND 0x34 +#define QUADSPI_BUF2IND 0x38 +#define QUADSPI_SFAR 0x100 + +#define QUADSPI_SMPR 0x108 +#define QUADSPI_SMPR_DDRSMP_SHIFT 16 +#define QUADSPI_SMPR_DDRSMP_MASK (7 << QUADSPI_SMPR_DDRSMP_SHIFT) +#define QUADSPI_SMPR_FSDLY_SHIFT 6 +#define QUADSPI_SMPR_FSDLY_MASK (1 << QUADSPI_SMPR_FSDLY_SHIFT) +#define QUADSPI_SMPR_FSPHS_SHIFT 5 +#define QUADSPI_SMPR_FSPHS_MASK (1 << QUADSPI_SMPR_FSPHS_SHIFT) +#define QUADSPI_SMPR_HSENA_SHIFT 0 +#define QUADSPI_SMPR_HSENA_MASK (1 << QUADSPI_SMPR_HSENA_SHIFT) + +#define QUADSPI_RBSR 0x10c +#define QUADSPI_RBSR_RDBFL_SHIFT 8 +#define QUADSPI_RBSR_RDBFL_MASK (0x3F << QUADSPI_RBSR_RDBFL_SHIFT) + +#define QUADSPI_RBCT 0x110 +#define QUADSPI_RBCT_WMRK_MASK 0x1F +#define QUADSPI_RBCT_RXBRD_SHIFT 8 +#define QUADSPI_RBCT_RXBRD_USEIPS (0x1 << QUADSPI_RBCT_RXBRD_SHIFT) + +#define QUADSPI_TBSR 0x150 +#define QUADSPI_TBDR 0x154 +#define QUADSPI_SR 0x15c +#define QUADSPI_SR_IP_ACC_SHIFT 1 +#define QUADSPI_SR_IP_ACC_MASK (0x1 << QUADSPI_SR_IP_ACC_SHIFT) +#define QUADSPI_SR_AHB_ACC_SHIFT 2 +#define QUADSPI_SR_AHB_ACC_MASK (0x1 << QUADSPI_SR_AHB_ACC_SHIFT) + +#define QUADSPI_FR 0x160 +#define QUADSPI_FR_TFF_MASK 0x1 + +#define QUADSPI_SFA1AD 0x180 +#define QUADSPI_SFA2AD 0x184 +#define QUADSPI_SFB1AD 0x188 +#define QUADSPI_SFB2AD 0x18c +#define QUADSPI_RBDR 0x200 + +#define QUADSPI_LUTKEY 0x300 +#define QUADSPI_LUTKEY_VALUE 0x5AF05AF0 + +#define QUADSPI_LCKCR 0x304 +#define QUADSPI_LCKER_LOCK 0x1 +#define QUADSPI_LCKER_UNLOCK 0x2 + +#define QUADSPI_RSER 0x164 +#define QUADSPI_RSER_TFIE (0x1 << 0) + +#define QUADSPI_LUT_BASE 0x310 + +/* + * The definition of the LUT register shows below: + * + * --------------------------------------------------- + * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | + * --------------------------------------------------- + */ +#define OPRND0_SHIFT 0 +#define PAD0_SHIFT 8 +#define INSTR0_SHIFT 10 +#define OPRND1_SHIFT 16 + +/* Instruction set for the LUT register. */ +#define LUT_STOP 0 +#define LUT_CMD 1 +#define LUT_ADDR 2 +#define LUT_DUMMY 3 +#define LUT_MODE 4 +#define LUT_MODE2 5 +#define LUT_MODE4 6 +#define LUT_READ 7 +#define LUT_WRITE 8 +#define LUT_JMP_ON_CS 9 +#define LUT_ADDR_DDR 10 +#define LUT_MODE_DDR 11 +#define LUT_MODE2_DDR 12 +#define LUT_MODE4_DDR 13 +#define LUT_READ_DDR 14 +#define LUT_WRITE_DDR 15 +#define LUT_DATA_LEARN 16 + +/* + * The PAD definitions for LUT register. + * + * The pad stands for the lines number of IO[0:3]. + * For example, the Quad read need four IO lines, so you should + * set LUT_PAD4 which means we use four IO lines. + */ +#define LUT_PAD1 0 +#define LUT_PAD2 1 +#define LUT_PAD4 2 + +/* Oprands for the LUT register. */ +#define ADDR24BIT 0x18 +#define ADDR32BIT 0x20 + +/* Macros for constructing the LUT register. */ +#define LUT0(ins, pad, opr) \ + (((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \ + ((LUT_##ins) << INSTR0_SHIFT)) + +#define LUT1(ins, pad, opr) (LUT0(ins, pad, opr) << OPRND1_SHIFT) + +/* other macros for LUT register. */ +#define QUADSPI_LUT(x) (QUADSPI_LUT_BASE + (x) * 4) +#define QUADSPI_LUT_NUM 64 + +/* SEQID -- we can have 16 seqids at most. */ +#define SEQID_QUAD_READ 0 +#define SEQID_WREN 1 +#define SEQID_WRDI 2 +#define SEQID_RDSR 3 +#define SEQID_SE 4 +#define SEQID_CHIP_ERASE 5 +#define SEQID_PP 6 +#define SEQID_RDID 7 +#define SEQID_WRSR 8 +#define SEQID_RDCR 9 +#define SEQID_EN4B 10 +#define SEQID_BRWR 11 + +enum fsl_qspi_devtype { + FSL_QUADSPI_VYBRID, + FSL_QUADSPI_IMX6SX, +}; + +struct fsl_qspi_devtype_data { + enum fsl_qspi_devtype devtype; + int rxfifo; + int txfifo; +}; + +static struct fsl_qspi_devtype_data vybrid_data = { + .devtype = FSL_QUADSPI_VYBRID, + .rxfifo = 128, + .txfifo = 64 +}; + +static struct fsl_qspi_devtype_data imx6sx_data = { + .devtype = FSL_QUADSPI_IMX6SX, + .rxfifo = 128, + .txfifo = 512 +}; + +#define FSL_QSPI_MAX_CHIP 4 +struct fsl_qspi { + struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; + struct spi_nor nor[FSL_QSPI_MAX_CHIP]; + void __iomem *iobase; + void __iomem *ahb_base; /* Used when read from AHB bus */ + u32 memmap_phy; + struct clk *clk, *clk_en; + struct device *dev; + struct completion c; + struct fsl_qspi_devtype_data *devtype_data; + u32 nor_size; + u32 nor_num; + u32 clk_rate; + unsigned int chip_base_addr; /* We may support two chips. */ +}; + +static inline int is_vybrid_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_VYBRID; +} + +static inline int is_imx6sx_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX; +} + +/* + * An IC bug makes us to re-arrange the 32-bit data. + * The following chips, such as IMX6SLX, have fixed this bug. + */ +static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) +{ + return is_vybrid_qspi(q) ? __swab32(a) : a; +} + +static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) +{ + writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); +} + +static inline void fsl_qspi_lock_lut(struct fsl_qspi *q) +{ + writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); +} + +static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) +{ + struct fsl_qspi *q = dev_id; + u32 reg; + + /* clear interrupt */ + reg = readl(q->iobase + QUADSPI_FR); + writel(reg, q->iobase + QUADSPI_FR); + + if (reg & QUADSPI_FR_TFF_MASK) + complete(&q->c); + + dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x:0x%.8x\n", q->chip_base_addr, reg); + return IRQ_HANDLED; +} + +static void fsl_qspi_init_lut(struct fsl_qspi *q) +{ + void *__iomem base = q->iobase; + int rxfifo = q->devtype_data->rxfifo; + u32 lut_base; + u8 cmd, addrlen, dummy; + int i; + + fsl_qspi_unlock_lut(q); + + /* Clear all the LUT table */ + for (i = 0; i < QUADSPI_LUT_NUM; i++) + writel(0, base + QUADSPI_LUT_BASE + i * 4); + + /* Quad Read */ + lut_base = SEQID_QUAD_READ * 4; + + if (q->nor_size <= SZ_16M) { + cmd = OPCODE_QUAD_READ; + addrlen = ADDR24BIT; + dummy = 8; + } else { + /* use the 4-byte address */ + cmd = OPCODE_QUAD_READ; + addrlen = ADDR32BIT; + dummy = 8; + } + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + + /* Write enable */ + lut_base = SEQID_WREN * 4; + writel(LUT0(CMD, PAD1, OPCODE_WREN), base + QUADSPI_LUT(lut_base)); + + /* Page Program */ + lut_base = SEQID_PP * 4; + + if (q->nor_size <= SZ_16M) { + cmd = OPCODE_PP; + addrlen = ADDR24BIT; + } else { + /* use the 4-byte address */ + cmd = OPCODE_PP; + addrlen = ADDR32BIT; + } + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); + + /* Read Status */ + lut_base = SEQID_RDSR * 4; + writel(LUT0(CMD, PAD1, OPCODE_RDSR) | LUT1(READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Erase a sector */ + lut_base = SEQID_SE * 4; + + if (q->nor_size <= SZ_16M) { + cmd = OPCODE_SE; + addrlen = ADDR24BIT; + } else { + /* use the 4-byte address */ + cmd = OPCODE_SE; + addrlen = ADDR32BIT; + } + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + /* Erase the whole chip */ + lut_base = SEQID_CHIP_ERASE * 4; + writel(LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), + base + QUADSPI_LUT(lut_base)); + + /* READ ID */ + lut_base = SEQID_RDID * 4; + writel(LUT0(CMD, PAD1, OPCODE_RDID) | LUT1(READ, PAD1, 0x8), + base + QUADSPI_LUT(lut_base)); + + /* Write Register */ + lut_base = SEQID_WRSR * 4; + writel(LUT0(CMD, PAD1, OPCODE_WRSR) | LUT1(WRITE, PAD1, 0x2), + base + QUADSPI_LUT(lut_base)); + + /* Read Configuration Register */ + lut_base = SEQID_RDCR * 4; + writel(LUT0(CMD, PAD1, OPCODE_RDCR) | LUT1(READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Write disable */ + lut_base = SEQID_WRDI * 4; + writel(LUT0(CMD, PAD1, OPCODE_WRDI), base + QUADSPI_LUT(lut_base)); + + /* Enter 4 Byte Mode (Micron) */ + lut_base = SEQID_EN4B * 4; + writel(LUT0(CMD, PAD1, OPCODE_EN4B), base + QUADSPI_LUT(lut_base)); + + /* Enter 4 Byte Mode (Spansion) */ + lut_base = SEQID_BRWR * 4; + writel(LUT0(CMD, PAD1, OPCODE_BRWR), base + QUADSPI_LUT(lut_base)); + + fsl_qspi_lock_lut(q); +} + +/* Get the SEQID for the command */ +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) +{ + switch (cmd) { + case OPCODE_QUAD_READ: + return SEQID_QUAD_READ; + case OPCODE_WREN: + return SEQID_WREN; + case OPCODE_WRDI: + return SEQID_WRDI; + case OPCODE_RDSR: + return SEQID_RDSR; + case OPCODE_SE: + return SEQID_SE; + case OPCODE_CHIP_ERASE: + return SEQID_CHIP_ERASE; + case OPCODE_PP: + return SEQID_PP; + case OPCODE_RDID: + return SEQID_RDID; + case OPCODE_WRSR: + return SEQID_WRSR; + case OPCODE_RDCR: + return SEQID_RDCR; + case OPCODE_EN4B: + return SEQID_EN4B; + case OPCODE_BRWR: + return SEQID_BRWR; + default: + dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); + break; + } + return -EINVAL; +} + +static int +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) +{ + void *__iomem base = q->iobase; + int seqid; + u32 reg, reg2; + int err; + + init_completion(&q->c); + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n", + q->chip_base_addr, addr, len, cmd); + + /* save the reg */ + reg = readl(base + QUADSPI_MCR); + + writel(q->memmap_phy + q->chip_base_addr + addr, base + QUADSPI_SFAR); + writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, + base + QUADSPI_RBCT); + writel(reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); + + do { + reg2 = readl(base + QUADSPI_SR); + if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) { + udelay(1); + dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2); + continue; + } + break; + } while (1); + + /* trigger the LUT now */ + seqid = fsl_qspi_get_seqid(q, cmd); + writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR); + + /* Wait for the interrupt. */ + err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000)); + if (!err) { + dev_err(q->dev, + "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", + cmd, addr, readl(base + QUADSPI_FR), + readl(base + QUADSPI_SR)); + err = -ETIMEDOUT; + } else { + err = 0; + } + + /* restore the MCR */ + writel(reg, base + QUADSPI_MCR); + + return err; +} + +/* Read out the data from the QUADSPI_RBDR buffer registers. */ +static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf) +{ + u32 tmp; + int i = 0; + + while (len > 0) { + tmp = readl(q->iobase + QUADSPI_RBDR + i * 4); + tmp = fsl_qspi_endian_xchg(q, tmp); + dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n", + q->chip_base_addr, tmp); + + if (len >= 4) { + *((u32 *)rxbuf) = tmp; + rxbuf += 4; + } else { + memcpy(rxbuf, &tmp, len); + break; + } + + len -= 4; + i++; + } +} + +/* + * If we have changed the content of the flash by writing or erasing, + * we need to invalidate the AHB buffer. If we do not do so, we may read out + * the wrong data. The spec tells us reset the AHB domain and Serial Flash + * domain at the same time. + */ +static inline void fsl_qspi_invalid(struct fsl_qspi *q) +{ + u32 reg; + + reg = readl(q->iobase + QUADSPI_MCR); + reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK; + writel(reg, q->iobase + QUADSPI_MCR); + + /* + * The minimum delay : 1 AHB + 2 SFCK clocks. + * Delay 1 us is enough. + */ + udelay(1); + + reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK); + writel(reg, q->iobase + QUADSPI_MCR); +} + +static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, + u8 opcode, unsigned int to, u32 *txbuf, + unsigned count, size_t *retlen) +{ + int ret, i, j; + u32 tmp; + + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n", + q->chip_base_addr, to, count); + + /* clear the TX FIFO. */ + tmp = readl(q->iobase + QUADSPI_MCR); + writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); + + /* fill the TX data to the FIFO */ + for (j = 0, i = ((count + 3) / 4); j < i; j++) { + tmp = fsl_qspi_endian_xchg(q, *txbuf); + writel(tmp, q->iobase + QUADSPI_TBDR); + txbuf++; + } + + /* Trigger it */ + ret = fsl_qspi_runcmd(q, opcode, to, count); + + if (ret == 0 && retlen) + *retlen += count; + + return ret; +} + +static void fsl_qspi_set_map_addr(struct fsl_qspi *q) +{ + int nor_size = q->nor_size; + void __iomem *base = q->iobase; + + writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); + writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); + writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); + writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); +} + +/* + * There are two different ways to read out the data from the flash: + * the "IP Command Read" and the "AHB Command Read". + * + * The IC guy suggests we use the "AHB Command Read" which is faster + * then the "IP Command Read". (What's more is that there is a bug in + * the "IP Command Read" in the Vybrid.) + * + * After we set up the registers for the "AHB Command Read", we can use + * the memcpy to read the data directly. A "missed" access to the buffer + * causes the controller to clear the buffer, and use the sequence pointed + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. + */ +static void fsl_qspi_init_abh_read(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + int seqid; + + /* AHB configuration for access buffer 0/1/2 .*/ + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); + writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); + + /* We only use the buffer3 */ + writel(0, base + QUADSPI_BUF0IND); + writel(0, base + QUADSPI_BUF1IND); + writel(0, base + QUADSPI_BUF2IND); + + /* Set the default lut sequence for AHB Read. */ + seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode); + writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT, + q->iobase + QUADSPI_BFGENCR); +} + +/* We use this function to do some basic init for spi_nor_scan(). */ +static int fsl_qspi_nor_setup(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + u32 reg; + int ret; + + /* the default frequency, we will change it in the future.*/ + ret = clk_set_rate(q->clk, 66000000); + if (ret) + return ret; + + /* Init the LUT table. */ + fsl_qspi_init_lut(q); + + /* Disable the module */ + writel(QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, + base + QUADSPI_MCR); + + reg = readl(base + QUADSPI_SMPR); + writel(reg & ~(QUADSPI_SMPR_FSDLY_MASK + | QUADSPI_SMPR_FSPHS_MASK + | QUADSPI_SMPR_HSENA_MASK + | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR); + + /* Enable the module */ + writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, + base + QUADSPI_MCR); + + /* enable the interrupt */ + writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); + + return 0; +} + +static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) +{ + unsigned long rate = q->clk_rate; + int ret; + + if (is_imx6sx_qspi(q)) + rate *= 4; + + ret = clk_set_rate(q->clk, rate); + if (ret) + return ret; + + /* Init the LUT table again. */ + fsl_qspi_init_lut(q); + + /* Init for AHB read */ + fsl_qspi_init_abh_read(q); + + return 0; +} + +static struct of_device_id fsl_qspi_dt_ids[] = { + { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, + { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); + +static void fsl_qspi_set_base_addr(struct fsl_qspi *q, struct spi_nor *nor) +{ + q->chip_base_addr = q->nor_size * (nor - q->nor); +} + +static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + int ret; + struct fsl_qspi *q = nor->priv; + + ret = fsl_qspi_runcmd(q, opcode, 0, len); + if (ret) + return ret; + + fsl_qspi_read_data(q, len, buf); + return 0; +} + +static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + if (!buf) { + ret = fsl_qspi_runcmd(q, opcode, 0, 1); + if (ret) + return ret; + + if (opcode == OPCODE_CHIP_ERASE) + fsl_qspi_invalid(q); + + } else if (len > 0) { + ret = fsl_qspi_nor_write(q, nor, opcode, 0, + (u32 *)buf, len, NULL); + } else { + dev_err(q->dev, "invalid cmd %d\n", opcode); + ret = -EINVAL; + } + + return ret; +} + +static void fsl_qspi_write(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, const u_char *buf) +{ + struct fsl_qspi *q = nor->priv; + + fsl_qspi_nor_write(q, nor, nor->program_opcode, to, + (u32 *)buf, len, retlen); + + /* invalid the data in the AHB buffer. */ + fsl_qspi_invalid(q); +} + +static int fsl_qspi_read(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct fsl_qspi *q = nor->priv; + u8 cmd = nor->read_opcode; + int ret; + + dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", + cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); + + /* Wait until the previous command is finished. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + /* Read out the data directly from the AHB buffer.*/ + memcpy(buf, q->ahb_base + q->chip_base_addr + from, len); + + *retlen += len; + return 0; +} + +static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n", + nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs); + + /* Wait until finished previous write command. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + if (ret) + return ret; + + ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); + if (ret) + return ret; + + fsl_qspi_invalid(q); + return 0; +} + +static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + ret = clk_enable(q->clk_en); + if (ret) + return ret; + + ret = clk_enable(q->clk); + if (ret) { + clk_disable(q->clk_en); + return ret; + } + + fsl_qspi_set_base_addr(q, nor); + return 0; +} + +static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct fsl_qspi *q = nor->priv; + + clk_disable(q->clk); + clk_disable(q->clk_en); +} + +static int fsl_qspi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata; + struct device *dev = &pdev->dev; + struct fsl_qspi *q; + struct resource *res; + struct spi_nor *nor; + struct mtd_info *mtd; + int ret, i = 0; + bool has_second_chip = false; + const struct of_device_id *of_id = + of_match_device(fsl_qspi_dt_ids, &pdev->dev); + + q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL); + if (!q) + return -ENOMEM; + + q->nor_num = of_get_child_count(dev->of_node); + if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP) + return -ENODEV; + + /* find the resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); + q->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(q->iobase)) { + ret = PTR_ERR(q->iobase); + goto map_failed; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "QuadSPI-memory"); + q->ahb_base = devm_ioremap_resource(dev, res); + if (IS_ERR(q->ahb_base)) { + ret = PTR_ERR(q->ahb_base); + goto map_failed; + } + q->memmap_phy = res->start; + + /* find the clocks */ + q->clk_en = devm_clk_get(dev, "qspi_en"); + if (IS_ERR(q->clk_en)) { + ret = PTR_ERR(q->clk_en); + goto map_failed; + } + + q->clk = devm_clk_get(dev, "qspi"); + if (IS_ERR(q->clk)) { + ret = PTR_ERR(q->clk); + goto map_failed; + } + + ret = clk_prepare_enable(q->clk_en); + if (ret) { + dev_err(dev, "can not enable the qspi_en clock\n"); + goto map_failed; + } + + ret = clk_prepare_enable(q->clk); + if (ret) { + clk_disable_unprepare(q->clk_en); + dev_err(dev, "can not enable the qspi clock\n"); + goto map_failed; + } + + /* find the irq */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "failed to get the irq\n"); + goto irq_failed; + } + + ret = devm_request_irq(dev, ret, + fsl_qspi_irq_handler, 0, pdev->name, q); + if (ret) { + dev_err(dev, "failed to request irq.\n"); + goto irq_failed; + } + + q->dev = dev; + q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; + platform_set_drvdata(pdev, q); + + ret = fsl_qspi_nor_setup(q); + if (ret) + goto irq_failed; + + if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) + has_second_chip = true; + + /* iterate the subnodes. */ + for_each_available_child_of_node(dev->of_node, np) { + const struct spi_device_id *id; + char modalias[40]; + + /* skip the holes */ + if (!has_second_chip) + i *= 2; + + nor = &q->nor[i]; + mtd = &q->mtd[i]; + + nor->mtd = mtd; + nor->dev = dev; + nor->priv = q; + mtd->priv = nor; + + /* fill the hooks */ + nor->read_reg = fsl_qspi_read_reg; + nor->write_reg = fsl_qspi_write_reg; + nor->read = fsl_qspi_read; + nor->write = fsl_qspi_write; + nor->erase = fsl_qspi_erase; + + nor->prepare = fsl_qspi_prep; + nor->unprepare = fsl_qspi_unprep; + + if (of_modalias_node(np, modalias, sizeof(modalias)) < 0) + goto map_failed; + + id = spi_nor_match_id(modalias); + if (!id) + goto map_failed; + + ret = of_property_read_u32(np, "spi-max-frequency", + &q->clk_rate); + if (ret < 0) + goto map_failed; + + /* set the chip address for READID */ + fsl_qspi_set_base_addr(q, nor); + + ret = spi_nor_scan(nor, id, SPI_NOR_QUAD); + if (ret) + goto map_failed; + + ppdata.of_node = np; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) + goto map_failed; + + /* Set the correct NOR size now. */ + if (q->nor_size == 0) { + q->nor_size = mtd->size; + + /* Map the SPI NOR to accessiable address */ + fsl_qspi_set_map_addr(q); + } + + /* + * The TX FIFO is 64 bytes in the Vybrid, but the Page Program + * may writes 265 bytes per time. The write is working in the + * unit of the TX FIFO, not in the unit of the SPI NOR's page + * size. + * + * So shrink the spi_nor->page_size if it is larger then the + * TX FIFO. + */ + if (nor->page_size > q->devtype_data->txfifo) + nor->page_size = q->devtype_data->txfifo; + + i++; + } + + /* finish the rest init. */ + ret = fsl_qspi_nor_setup_last(q); + if (ret) + goto last_init_failed; + + clk_disable(q->clk); + clk_disable(q->clk_en); + dev_info(dev, "QuadSPI SPI NOR flash driver\n"); + return 0; + +last_init_failed: + for (i = 0; i < q->nor_num; i++) + mtd_device_unregister(&q->mtd[i]); + +irq_failed: + clk_disable_unprepare(q->clk); + clk_disable_unprepare(q->clk_en); +map_failed: + dev_err(dev, "Freescale QuadSPI probe failed\n"); + return ret; +} + +static int fsl_qspi_remove(struct platform_device *pdev) +{ + struct fsl_qspi *q = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < q->nor_num; i++) + mtd_device_unregister(&q->mtd[i]); + + /* disable the hardware */ + writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + writel(0x0, q->iobase + QUADSPI_RSER); + + clk_unprepare(q->clk); + clk_unprepare(q->clk_en); + return 0; +} + +static struct platform_driver fsl_qspi_driver = { + .driver = { + .name = "fsl-quadspi", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .of_match_table = fsl_qspi_dt_ids, + }, + .probe = fsl_qspi_probe, + .remove = fsl_qspi_remove, +}; +module_platform_driver(fsl_qspi_driver); + +MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver"); +MODULE_AUTHOR("Freescale Semiconductor Inc."); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From b61834b0d0ed504b9340a55c88977cb9539454df Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:22:57 -0700 Subject: mtd: spi-nor: EXPORT symbols which could be used by module drivers Fix errors like this: ERROR: "spi_nor_ids" [drivers/mtd/devices/m25p80.ko] undefined! ERROR: "spi_nor_scan" [drivers/mtd/devices/m25p80.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index f7c9e638623b..5cd86eb2a5f0 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -584,6 +584,7 @@ const struct spi_device_id spi_nor_ids[] = { { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { }, }; +EXPORT_SYMBOL_GPL(spi_nor_ids); static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) { @@ -1081,6 +1082,7 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, mtd->eraseregions[i].numblocks); return 0; } +EXPORT_SYMBOL_GPL(spi_nor_scan); const struct spi_device_id *spi_nor_match_id(char *name) { @@ -1093,6 +1095,7 @@ const struct spi_device_id *spi_nor_match_id(char *name) } return NULL; } +EXPORT_SYMBOL_GPL(spi_nor_match_id); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huang Shijie "); -- cgit v1.2.3 From 5fa980691bb4f61e3c2ca9ca451111be4e06caa2 Mon Sep 17 00:00:00 2001 From: Angus Clark Date: Wed, 26 Mar 2014 16:39:15 +0000 Subject: mtd: st_spi_fsm: Add Macronix MX25L25655E device Add Macronix MX25L25655E to the list of known devices. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 1957d7c8e185..d7e38cc8814f 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -380,6 +380,9 @@ static struct flash_info flash_types[] = { { "mx25l25635e", 0xc22019, 0, 64*1024, 512, (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, stfsm_mx25_config }, + { "mx25l25655e", 0xc22619, 0, 64*1024, 512, + (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, + stfsm_mx25_config}, #define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \ FLASH_FLAG_READ_FAST | \ -- cgit v1.2.3 From 009e7e61b1f15b16964f1b4b6bb1e30e6b03dc3b Mon Sep 17 00:00:00 2001 From: Angus Clark Date: Wed, 26 Mar 2014 16:39:16 +0000 Subject: mtd: st_spi_fsm: Update Macronix 32-bit addressing support Support for the Macronix 32-bit addressing scheme was originally developed using the MX25L25635E device. As is often the case, it was found that the presence of a "WAIT" instruction was required for the "EN4B/EX4B" FSM Sequence to complete. (It is known that the SPI FSM Controller makes certain undocumented assumptions regarding what constitutes a valid sequence.) However, further testing suggested that a small delay was required after issuing the "EX4B" command; without this delay, data corruptions were observed, consistent with the device not being ready to retrieve data. Although the issue was not fully understood, the workaround of adding a small delay was implemented, while awaiting clarification from Macronix. The same behaviour has now been found with a second Macronix device, the MX25L25655E. However, with this device, it seems that the delay is also required after the 'EN4B' commands. This discovery has prompted us to revisit the issue. Although still not conclusive, further tests have suggested that the issue is down to the SPI FSM Controller, rather than the Macronix devices. Furthermore, an alternative workaround has emerged which is to set the WAIT time to 0x00000001, rather then 0x00000000. (Note, the WAIT instruction is used purely for the purpose of achieving "sequence validity", rather than actually implementing a delay!) The issue is now being investigated by the Design and Validation teams. In the meantime, we implement the alternative workaround, which reduces the effective delay from 1us to 1ns. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index d7e38cc8814f..40eb830eea21 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -270,7 +270,6 @@ */ #define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001 #define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002 -#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004 #define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008 #define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010 @@ -1152,23 +1151,17 @@ static int stfsm_mx25_config(struct stfsm *fsm) stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); soc_reset = stfsm_can_handle_soc_reset(fsm); - if (soc_reset || !fsm->booted_from_spi) { + if (soc_reset || !fsm->booted_from_spi) /* If we can handle SoC resets, we enable 32-bit address * mode pervasively */ stfsm_enter_32bit_addr(fsm, 1); - } else { + else /* Else, enable/disable 32-bit addressing before/after * each operation */ fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR | CFG_WRITE_TOGGLE_32BIT_ADDR | CFG_ERASESEC_TOGGLE_32BIT_ADDR); - /* It seems a small delay is required after exiting - * 32-bit mode following a write operation. The issue - * is under investigation. - */ - fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY; - } } /* For QUAD mode, set 'QE' STATUS bit */ @@ -1631,11 +1624,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, stfsm_s25fl_clear_status_reg(fsm); /* Exit 32-bit address mode, if required */ - if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) { + if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) stfsm_enter_32bit_addr(fsm, 0); - if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY) - udelay(1); - } return 0; } @@ -1938,6 +1928,13 @@ static int stfsm_init(struct stfsm *fsm) fsm->base + SPI_CONFIGDATA); writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG); + /* + * Set the FSM 'WAIT' delay to the minimum workable value. Note, for + * our purposes, the WAIT instruction is used purely to achieve + * "sequence validity" rather than actually implement a delay. + */ + writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME); + /* Clear FIFO, just in case */ stfsm_clear_fifo(fsm); -- cgit v1.2.3 From cc6668637ebed620eaafc807802f3d85b193f534 Mon Sep 17 00:00:00 2001 From: Angus Clark Date: Wed, 26 Mar 2014 16:39:17 +0000 Subject: mtd: st_spi_fsm: Update Macronix 'QE' configuration Update the configuration of the Macronix 'QE' bit, such that we only set or clear the bit if required. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 40eb830eea21..f86d3e01433b 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -1164,12 +1164,27 @@ static int stfsm_mx25_config(struct stfsm *fsm) CFG_ERASESEC_TOGGLE_32BIT_ADDR); } - /* For QUAD mode, set 'QE' STATUS bit */ + /* Check status of 'QE' bit */ + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; if (data_pads == 4) { - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta); - sta |= MX25_STATUS_QE; - stfsm_write_status(fsm, sta, 1); + if (!(sta & MX25_STATUS_QE)) { + /* Set 'QE' */ + sta |= MX25_STATUS_QE; + + stfsm_write_status(fsm, sta, 1); + + stfsm_wait_busy(fsm); + } + } else { + if (sta & MX25_STATUS_QE) { + /* Clear 'QE' */ + sta &= ~MX25_STATUS_QE; + + stfsm_write_status(fsm, sta, 1); + + stfsm_wait_busy(fsm); + } } return 0; -- cgit v1.2.3 From 5d0bddab397ff705d66e6f87b61ea8abc3c62948 Mon Sep 17 00:00:00 2001 From: Angus Clark Date: Wed, 26 Mar 2014 16:39:18 +0000 Subject: mtd: st_spi_fsm: Refactor status register operations This patch refactors the fsm_read_status() and fsm_write_status() code to support 1 or 2 byte operations, with a specified command. This allows us to remove device/register specific code, such as the N25Q fsm_wrvcr() function. The 'QE' configuration code is updated accordingly, with minor tweaks to ensure the register values are only written if actually required. One notable change in this area is that the 'W25Q_STATUS_QE' bit-field is now defined with respect to the 'SR2' register, rather than the combined 'SR1+SR2' register which is only used for write operations. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 152 ++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 84 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index f86d3e01433b..ced73b13f46b 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -212,8 +212,6 @@ #define FLASH_CMD_SE_32K 0x52 #define FLASH_CMD_SE 0xd8 #define FLASH_CMD_CHIPERASE 0xc7 -#define FLASH_CMD_WRVCR 0x81 -#define FLASH_CMD_RDVCR 0x85 #define FLASH_CMD_READ 0x03 /* READ */ #define FLASH_CMD_READ_FAST 0x0b /* FAST READ */ @@ -261,6 +259,12 @@ #define S25FL_STATUS_E_ERR 0x20 #define S25FL_STATUS_P_ERR 0x40 +#define N25Q_CMD_WRVCR 0x81 +#define N25Q_CMD_RDVCR 0x85 +#define N25Q_CMD_RDVECR 0x65 +#define N25Q_CMD_RDNVCR 0xb5 +#define N25Q_CMD_WRNVCR 0xb1 + #define FLASH_PAGESIZE 256 /* In Bytes */ #define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */ #define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */ @@ -592,7 +596,7 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = { /* * [W25Qxxx] Configuration */ -#define W25Q_STATUS_QE (0x1 << 9) +#define W25Q_STATUS_QE (0x1 << 1) static struct stfsm_seq stfsm_seq_read_jedec = { .data_size = TRANSFER_SIZE(8), @@ -686,23 +690,6 @@ static struct stfsm_seq stfsm_seq_write_status = { SEQ_CFG_STARTSEQ), }; -static struct stfsm_seq stfsm_seq_wrvcr = { - .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), - .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)), - .seq = { - STFSM_INST_CMD1, - STFSM_INST_CMD2, - STFSM_INST_STA_WR1, - STFSM_INST_STOP, - }, - .seq_cfg = (SEQ_CFG_PADS_1 | - SEQ_CFG_READNOTWRITE | - SEQ_CFG_CSDEASSERT | - SEQ_CFG_STARTSEQ), -}; - static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | @@ -891,60 +878,57 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) } static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd, - uint8_t *status) + uint8_t *data, int bytes) { struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; uint32_t tmp; + uint8_t *t = (uint8_t *)&tmp; + int i; - dev_dbg(fsm->dev, "reading STA[%s]\n", - (cmd == FLASH_CMD_RDSR) ? "1" : "2"); + dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n", + cmd, bytes); - seq->seq_opc[0] = (SEQ_OPC_PADS_1 | - SEQ_OPC_CYCLES(8) | + BUG_ON(bytes != 1 && bytes != 2); + + seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | SEQ_OPC_OPCODE(cmd)), stfsm_load_seq(fsm, seq); stfsm_read_fifo(fsm, &tmp, 4); - *status = (uint8_t)(tmp >> 24); + for (i = 0; i < bytes; i++) + data[i] = t[i]; stfsm_wait_seq(fsm); return 0; } -static int stfsm_write_status(struct stfsm *fsm, uint16_t status, - int sta_bytes) +static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd, + uint16_t data, int bytes, int wait_busy) { struct stfsm_seq *seq = &stfsm_seq_write_status; - dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n", - (sta_bytes == 1) ? "1" : "1+2", status); - - seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT; - seq->seq[2] = (sta_bytes == 1) ? - STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2; - - stfsm_load_seq(fsm, seq); - - stfsm_wait_seq(fsm); - - return 0; -}; + dev_dbg(fsm->dev, + "write 'status' register [0x%02x], %d byte(s), 0x%04x\n" + " %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no"); -static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data) -{ - struct stfsm_seq *seq = &stfsm_seq_wrvcr; + BUG_ON(bytes != 1 && bytes != 2); - dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data); + seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(cmd)); - seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT); + seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT; + seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2; stfsm_load_seq(fsm, seq); stfsm_wait_seq(fsm); + if (wait_busy) + stfsm_wait_busy(fsm); + return 0; } @@ -1164,26 +1148,22 @@ static int stfsm_mx25_config(struct stfsm *fsm) CFG_ERASESEC_TOGGLE_32BIT_ADDR); } - /* Check status of 'QE' bit */ - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta); + /* Check status of 'QE' bit, update if required. */ + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta, 1); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; if (data_pads == 4) { if (!(sta & MX25_STATUS_QE)) { /* Set 'QE' */ sta |= MX25_STATUS_QE; - stfsm_write_status(fsm, sta, 1); - - stfsm_wait_busy(fsm); + stfsm_write_status(fsm, FLASH_CMD_WRSR, sta, 1, 1); } } else { if (sta & MX25_STATUS_QE) { /* Clear 'QE' */ sta &= ~MX25_STATUS_QE; - stfsm_write_status(fsm, sta, 1); - - stfsm_wait_busy(fsm); + stfsm_write_status(fsm, FLASH_CMD_WRSR, sta, 1, 1); } } @@ -1250,7 +1230,7 @@ static int stfsm_n25q_config(struct stfsm *fsm) */ vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED | N25Q_VCR_WRAP_CONT); - stfsm_wrvcr(fsm, vcr); + stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0); return 0; } @@ -1378,6 +1358,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm) uint32_t offs; uint16_t sta_wr; uint8_t sr1, cr1, dyb; + int update_sr = 0; int ret; if (flags & FLASH_FLAG_32BIT_ADDR) { @@ -1425,34 +1406,28 @@ static int stfsm_s25fl_config(struct stfsm *fsm) } } - /* Check status of 'QE' bit */ + /* Check status of 'QE' bit, update if required. */ + stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1, 1); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; - stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1); if (data_pads == 4) { if (!(cr1 & STFSM_S25FL_CONFIG_QE)) { /* Set 'QE' */ cr1 |= STFSM_S25FL_CONFIG_QE; - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1); - sta_wr = ((uint16_t)cr1 << 8) | sr1; - - stfsm_write_status(fsm, sta_wr, 2); - - stfsm_wait_busy(fsm); + update_sr = 1; } } else { - if ((cr1 & STFSM_S25FL_CONFIG_QE)) { + if (cr1 & STFSM_S25FL_CONFIG_QE) { /* Clear 'QE' */ cr1 &= ~STFSM_S25FL_CONFIG_QE; - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1); - sta_wr = ((uint16_t)cr1 << 8) | sr1; - - stfsm_write_status(fsm, sta_wr, 2); - - stfsm_wait_busy(fsm); + update_sr = 1; } - + } + if (update_sr) { + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1, 1); + sta_wr = ((uint16_t)cr1 << 8) | sr1; + stfsm_write_status(fsm, FLASH_CMD_WRSR, sta_wr, 2, 1); } /* @@ -1467,27 +1442,36 @@ static int stfsm_s25fl_config(struct stfsm *fsm) static int stfsm_w25q_config(struct stfsm *fsm) { uint32_t data_pads; - uint16_t sta_wr; - uint8_t sta1, sta2; + uint8_t sr1, sr2; + uint16_t sr_wr; + int update_sr = 0; int ret; ret = stfsm_prepare_rwe_seqs_default(fsm); if (ret) return ret; - /* If using QUAD mode, set QE STATUS bit */ + /* Check status of 'QE' bit, update if required. */ + stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sr2, 1); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; if (data_pads == 4) { - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1); - stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2); - - sta_wr = ((uint16_t)sta2 << 8) | sta1; - - sta_wr |= W25Q_STATUS_QE; - - stfsm_write_status(fsm, sta_wr, 2); - - stfsm_wait_busy(fsm); + if (!(sr2 & W25Q_STATUS_QE)) { + /* Set 'QE' */ + sr2 |= W25Q_STATUS_QE; + update_sr = 1; + } + } else { + if (sr2 & W25Q_STATUS_QE) { + /* Clear 'QE' */ + sr2 &= ~W25Q_STATUS_QE; + update_sr = 1; + } + } + if (update_sr) { + /* Write status register */ + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1, 1); + sr_wr = ((uint16_t)sr2 << 8) | sr1; + stfsm_write_status(fsm, FLASH_CMD_WRSR, sr_wr, 2, 1); } return 0; -- cgit v1.2.3 From 85bdcf6b255e5197be6a6876d663256e9c164a78 Mon Sep 17 00:00:00 2001 From: Angus Clark Date: Wed, 26 Mar 2014 16:39:19 +0000 Subject: mtd: st_spi_fsm: Add Spansion S25FL032P to device table Add Spansion S25FL032P to the list of known devices. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index ced73b13f46b..a3d7334350d9 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -413,6 +413,8 @@ static struct flash_info flash_types[] = { FLASH_FLAG_READ_1_4_4 | \ FLASH_FLAG_WRITE_1_1_4 | \ FLASH_FLAG_READ_FAST) + { "s25fl032p", 0x010215, 0x4d00, 64 * 1024, 64, S25FLXXXP_FLAG, 80, + stfsm_s25fl_config}, { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80, stfsm_s25fl_config }, { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80, -- cgit v1.2.3 From 6b6d37377c4c4c5cd1d9505afbc4e2d3113707f2 Mon Sep 17 00:00:00 2001 From: Angus Clark Date: Wed, 26 Mar 2014 16:39:20 +0000 Subject: mtd: st_spi_fsm: Add support for Macronix MX25L3255E This patch adds support for the Macronix MX25L3255E device. Unlike the other Macronix devices we have seen, this device supports WRITE_1_4_4 at reasonable frequencies. Rather than masking out WRITE_1_4_4 support altogether, we now rely on the table parameters to indicate whether or not WRITE_1_4_4 should be used. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index a3d7334350d9..30082a09f68b 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -372,14 +372,20 @@ static struct flash_info flash_types[] = { { "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL }, { "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL }, + /* Macronix MX25xxx + * - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices + * where operating frequency must be reduced. + */ #define MX25_FLAG (FLASH_FLAG_READ_WRITE | \ FLASH_FLAG_READ_FAST | \ FLASH_FLAG_READ_1_1_2 | \ FLASH_FLAG_READ_1_2_2 | \ FLASH_FLAG_READ_1_1_4 | \ - FLASH_FLAG_READ_1_4_4 | \ FLASH_FLAG_SE_4K | \ FLASH_FLAG_SE_32K) + { "mx25l3255e", 0xc29e16, 0, 64 * 1024, 64, + (MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86, + stfsm_mx25_config}, { "mx25l25635e", 0xc22019, 0, 64*1024, 512, (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, stfsm_mx25_config }, -- cgit v1.2.3 From 8eabdd1ec122cf6b77cf73e798f134fbace1b8d1 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 10 Apr 2014 16:27:28 +0800 Subject: mtd: spi-nor: add the copyright information Add the copyright information for spi-nor.c and spi-nor.h. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5cd86eb2a5f0..6c64ab95dee2 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1,5 +1,9 @@ /* - * Cloned most of the code from the m25p80.c + * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with + * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c + * + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as -- cgit v1.2.3 From b02e7f3ef0beb72da8fc64542f0ac977996ec56b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:15:31 -0700 Subject: mtd: spi-nor: re-name OPCODE_* to SPINOR_OP_* Qualify these with a better namespace, and prepare them for use in more drivers. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie --- drivers/mtd/devices/m25p80.c | 4 +-- drivers/mtd/spi-nor/fsl-quadspi.c | 58 +++++++++++++++++------------------ drivers/mtd/spi-nor/spi-nor.c | 64 +++++++++++++++++++-------------------- 3 files changed, 63 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4af6400ccd95..1557d8f672c1 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -86,7 +86,7 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, spi_message_init(&m); - if (nor->program_opcode == OPCODE_AAI_WP && nor->sst_write_second) + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) cmd_sz = 1; flash->command[0] = nor->program_opcode; @@ -171,7 +171,7 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 6dc08ed950c8..2977f026f39d 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -294,12 +294,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) lut_base = SEQID_QUAD_READ * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_QUAD_READ; + cmd = SPINOR_OP_QUAD_READ; addrlen = ADDR24BIT; dummy = 8; } else { /* use the 4-byte address */ - cmd = OPCODE_QUAD_READ; + cmd = SPINOR_OP_QUAD_READ; addrlen = ADDR32BIT; dummy = 8; } @@ -311,17 +311,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Write enable */ lut_base = SEQID_WREN * 4; - writel(LUT0(CMD, PAD1, OPCODE_WREN), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base)); /* Page Program */ lut_base = SEQID_PP * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_PP; + cmd = SPINOR_OP_PP; addrlen = ADDR24BIT; } else { /* use the 4-byte address */ - cmd = OPCODE_PP; + cmd = SPINOR_OP_PP; addrlen = ADDR32BIT; } @@ -331,18 +331,18 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Read Status */ lut_base = SEQID_RDSR * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDSR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Erase a sector */ lut_base = SEQID_SE * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_SE; + cmd = SPINOR_OP_SE; addrlen = ADDR24BIT; } else { /* use the 4-byte address */ - cmd = OPCODE_SE; + cmd = SPINOR_OP_SE; addrlen = ADDR32BIT; } @@ -351,35 +351,35 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Erase the whole chip */ lut_base = SEQID_CHIP_ERASE * 4; - writel(LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), + writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), base + QUADSPI_LUT(lut_base)); /* READ ID */ lut_base = SEQID_RDID * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDID) | LUT1(READ, PAD1, 0x8), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), base + QUADSPI_LUT(lut_base)); /* Write Register */ lut_base = SEQID_WRSR * 4; - writel(LUT0(CMD, PAD1, OPCODE_WRSR) | LUT1(WRITE, PAD1, 0x2), + writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), base + QUADSPI_LUT(lut_base)); /* Read Configuration Register */ lut_base = SEQID_RDCR * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDCR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Write disable */ lut_base = SEQID_WRDI * 4; - writel(LUT0(CMD, PAD1, OPCODE_WRDI), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Micron) */ lut_base = SEQID_EN4B * 4; - writel(LUT0(CMD, PAD1, OPCODE_EN4B), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Spansion) */ lut_base = SEQID_BRWR * 4; - writel(LUT0(CMD, PAD1, OPCODE_BRWR), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + QUADSPI_LUT(lut_base)); fsl_qspi_lock_lut(q); } @@ -388,29 +388,29 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { - case OPCODE_QUAD_READ: + case SPINOR_OP_QUAD_READ: return SEQID_QUAD_READ; - case OPCODE_WREN: + case SPINOR_OP_WREN: return SEQID_WREN; - case OPCODE_WRDI: + case SPINOR_OP_WRDI: return SEQID_WRDI; - case OPCODE_RDSR: + case SPINOR_OP_RDSR: return SEQID_RDSR; - case OPCODE_SE: + case SPINOR_OP_SE: return SEQID_SE; - case OPCODE_CHIP_ERASE: + case SPINOR_OP_CHIP_ERASE: return SEQID_CHIP_ERASE; - case OPCODE_PP: + case SPINOR_OP_PP: return SEQID_PP; - case OPCODE_RDID: + case SPINOR_OP_RDID: return SEQID_RDID; - case OPCODE_WRSR: + case SPINOR_OP_WRSR: return SEQID_WRSR; - case OPCODE_RDCR: + case SPINOR_OP_RDCR: return SEQID_RDCR; - case OPCODE_EN4B: + case SPINOR_OP_EN4B: return SEQID_EN4B; - case OPCODE_BRWR: + case SPINOR_OP_BRWR: return SEQID_BRWR; default: dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); @@ -688,7 +688,7 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, if (ret) return ret; - if (opcode == OPCODE_CHIP_ERASE) + if (opcode == SPINOR_OP_CHIP_ERASE) fsl_qspi_invalid(q); } else if (len > 0) { @@ -750,7 +750,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 6c64ab95dee2..1716f3ce9949 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -38,7 +38,7 @@ static int read_sr(struct spi_nor *nor) int ret; u8 val; - ret = nor->read_reg(nor, OPCODE_RDSR, &val, 1); + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); if (ret < 0) { pr_err("error %d reading SR\n", (int) ret); return ret; @@ -57,7 +57,7 @@ static int read_cr(struct spi_nor *nor) int ret; u8 val; - ret = nor->read_reg(nor, OPCODE_RDCR, &val, 1); + ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1); if (ret < 0) { dev_err(nor->dev, "error %d reading CR\n", ret); return ret; @@ -91,7 +91,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) static inline int write_sr(struct spi_nor *nor, u8 val) { nor->cmd_buf[0] = val; - return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); } /* @@ -100,7 +100,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) */ static inline int write_enable(struct spi_nor *nor) { - return nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); } /* @@ -108,7 +108,7 @@ static inline int write_enable(struct spi_nor *nor) */ static inline int write_disable(struct spi_nor *nor) { - return nor->write_reg(nor, OPCODE_WRDI, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); } static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) @@ -132,7 +132,7 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) if (need_wren) write_enable(nor); - cmd = enable ? OPCODE_EN4B : OPCODE_EX4B; + cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; status = nor->write_reg(nor, cmd, NULL, 0, 0); if (need_wren) write_disable(nor); @@ -141,7 +141,7 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) default: /* Spansion style */ nor->cmd_buf[0] = enable << 7; - return nor->write_reg(nor, OPCODE_BRWR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); } } @@ -193,7 +193,7 @@ static int erase_chip(struct spi_nor *nor) /* Send write enable, then erase commands. */ write_enable(nor); - return nor->write_reg(nor, OPCODE_CHIP_ERASE, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); } static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -253,7 +253,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) } /* REVISIT in some cases we could speed up erasing large regions - * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up * to use "small sector erase", but that's not always optimal. */ @@ -385,7 +385,7 @@ struct flash_info { u32 jedec_id; u16 ext_id; - /* The size listed here is what works with OPCODE_SE, which isn't + /* The size listed here is what works with SPINOR_OP_SE, which isn't * necessarily called a "sector" by the vendor. */ unsigned sector_size; @@ -395,11 +395,11 @@ struct flash_info { u16 addr_width; u16 flags; -#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */ #define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ #define SST_WRITE 0x04 /* use SST byte programming */ #define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ -#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ }; @@ -598,7 +598,7 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) u16 ext_jedec; struct flash_info *info; - tmp = nor->read_reg(nor, OPCODE_RDID, id, 5); + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); if (tmp < 0) { dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); return ERR_PTR(tmp); @@ -670,7 +670,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, actual = to % 2; /* Start write from odd address. */ if (actual) { - nor->program_opcode = OPCODE_BP; + nor->program_opcode = SPINOR_OP_BP; /* write one byte. */ nor->write(nor, to, 1, retlen, buf); @@ -682,7 +682,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, /* Write out most of the data here. */ for (; actual < len - 1; actual += 2) { - nor->program_opcode = OPCODE_AAI_WP; + nor->program_opcode = SPINOR_OP_AAI_WP; /* write two bytes. */ nor->write(nor, to, 2, retlen, buf + actual); @@ -703,7 +703,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, if (actual != len) { write_enable(nor); - nor->program_opcode = OPCODE_BP; + nor->program_opcode = SPINOR_OP_BP; nor->write(nor, to, 1, retlen, buf + actual); ret = wait_till_ready(nor); @@ -777,7 +777,7 @@ static int macronix_quad_enable(struct spi_nor *nor) write_enable(nor); nor->cmd_buf[0] = val | SR_QUAD_EN_MX; - nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); if (wait_till_ready(nor)) return 1; @@ -802,7 +802,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val) nor->cmd_buf[0] = val & 0xff; nor->cmd_buf[1] = (val >> 8); - return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 2, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); } static int spansion_quad_enable(struct spi_nor *nor) @@ -967,13 +967,13 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { - nor->erase_opcode = OPCODE_BE_4K; + nor->erase_opcode = SPINOR_OP_BE_4K; mtd->erasesize = 4096; } else if (info->flags & SECT_4K_PMC) { - nor->erase_opcode = OPCODE_BE_4K_PMC; + nor->erase_opcode = SPINOR_OP_BE_4K_PMC; mtd->erasesize = 4096; } else { - nor->erase_opcode = OPCODE_SE; + nor->erase_opcode = SPINOR_OP_SE; mtd->erasesize = info->sector_size; } @@ -1014,23 +1014,23 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = OPCODE_QUAD_READ; + nor->read_opcode = SPINOR_OP_QUAD_READ; break; case SPI_NOR_DUAL: - nor->read_opcode = OPCODE_DUAL_READ; + nor->read_opcode = SPINOR_OP_DUAL_READ; break; case SPI_NOR_FAST: - nor->read_opcode = OPCODE_FAST_READ; + nor->read_opcode = SPINOR_OP_FAST_READ; break; case SPI_NOR_NORMAL: - nor->read_opcode = OPCODE_NORM_READ; + nor->read_opcode = SPINOR_OP_NORM_READ; break; default: dev_err(dev, "No Read opcode defined\n"); return -EINVAL; } - nor->program_opcode = OPCODE_PP; + nor->program_opcode = SPINOR_OP_PP; if (info->addr_width) nor->addr_width = info->addr_width; @@ -1041,21 +1041,21 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = OPCODE_QUAD_READ_4B; + nor->read_opcode = SPINOR_OP_QUAD_READ_4B; break; case SPI_NOR_DUAL: - nor->read_opcode = OPCODE_DUAL_READ_4B; + nor->read_opcode = SPINOR_OP_DUAL_READ_4B; break; case SPI_NOR_FAST: - nor->read_opcode = OPCODE_FAST_READ_4B; + nor->read_opcode = SPINOR_OP_FAST_READ_4B; break; case SPI_NOR_NORMAL: - nor->read_opcode = OPCODE_NORM_READ_4B; + nor->read_opcode = SPINOR_OP_NORM_READ_4B; break; } - nor->program_opcode = OPCODE_PP_4B; + nor->program_opcode = SPINOR_OP_PP_4B; /* No small sector erase for 4-byte command set */ - nor->erase_opcode = OPCODE_SE_4B; + nor->erase_opcode = SPINOR_OP_SE_4B; mtd->erasesize = info->sector_size; } else set_4byte(nor, info->jedec_id, 1); -- cgit v1.2.3 From 58b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 19:16:49 -0700 Subject: mtd: spi-nor: unify read opcode variants with ST SPI FSM serial_flash_cmds.h defines our opcodes a little differently. Let's borrow its naming, since it's borrowed from the SFDP standard, and it's more extensible. This prepares us for merging serial_flash_cmds.h and spi-nor.h opcode listing. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie --- drivers/mtd/spi-nor/fsl-quadspi.c | 6 +++--- drivers/mtd/spi-nor/spi-nor.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 2977f026f39d..b41bbbc531ff 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -294,12 +294,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) lut_base = SEQID_QUAD_READ * 4; if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_QUAD_READ; + cmd = SPINOR_OP_READ_1_1_4; addrlen = ADDR24BIT; dummy = 8; } else { /* use the 4-byte address */ - cmd = SPINOR_OP_QUAD_READ; + cmd = SPINOR_OP_READ_1_1_4; addrlen = ADDR32BIT; dummy = 8; } @@ -388,7 +388,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { - case SPINOR_OP_QUAD_READ: + case SPINOR_OP_READ_1_1_4: return SEQID_QUAD_READ; case SPINOR_OP_WREN: return SEQID_WREN; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1716f3ce9949..d6f44d527701 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1014,16 +1014,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = SPINOR_OP_QUAD_READ; + nor->read_opcode = SPINOR_OP_READ_1_1_4; break; case SPI_NOR_DUAL: - nor->read_opcode = SPINOR_OP_DUAL_READ; + nor->read_opcode = SPINOR_OP_READ_1_1_2; break; case SPI_NOR_FAST: - nor->read_opcode = SPINOR_OP_FAST_READ; + nor->read_opcode = SPINOR_OP_READ_FAST; break; case SPI_NOR_NORMAL: - nor->read_opcode = SPINOR_OP_NORM_READ; + nor->read_opcode = SPINOR_OP_READ; break; default: dev_err(dev, "No Read opcode defined\n"); @@ -1041,16 +1041,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = SPINOR_OP_QUAD_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_1_1_4; break; case SPI_NOR_DUAL: - nor->read_opcode = SPINOR_OP_DUAL_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_1_1_2; break; case SPI_NOR_FAST: - nor->read_opcode = SPINOR_OP_FAST_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_FAST; break; case SPI_NOR_NORMAL: - nor->read_opcode = SPINOR_OP_NORM_READ_4B; + nor->read_opcode = SPINOR_OP_READ4; break; } nor->program_opcode = SPINOR_OP_PP_4B; -- cgit v1.2.3 From e43b20619bdb6c851dd7b49cbd15e52875a785d4 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 20:30:25 -0700 Subject: mtd: spi-nor: shorten Kconfig naming Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie --- drivers/mtd/Makefile | 2 +- drivers/mtd/devices/Kconfig | 2 +- drivers/mtd/spi-nor/Kconfig | 11 ++++++++--- drivers/mtd/spi-nor/Makefile | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 40fd15344387..99bb9a1f6e16 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -32,5 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ -obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor/ +obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 48aa1aa5820b..f9e32b53dfc6 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP config MTD_M25P80 tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" - depends on SPI_MASTER && MTD_SPI_NOR_BASE + depends on SPI_MASTER && MTD_SPI_NOR help This enables access to most modern SPI flash chips, used for program and data storage. Series supported include Atmel AT26DF, diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 64cfc39ad17e..d682fb440e06 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,12 +1,17 @@ -config MTD_SPI_NOR_BASE - bool "the framework for SPI-NOR support" +config MTD_SPI_NOR + bool "SPI-NOR device support" depends on MTD help This is the framework for the SPI NOR which can be used by the SPI device drivers and the SPI-NOR device driver. + +if MTD_SPI_NOR + config SPI_FSL_QUADSPI tristate "Freescale Quad SPI controller" - depends on ARCH_MXC && MTD_SPI_NOR_BASE + depends on ARCH_MXC help This enables support for the Quad SPI controller in master mode. We only connect the NOR to this controller now. + +endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 51f9d8b99ab7..6a7ce1462247 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o +obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o -- cgit v1.2.3 From 8607bd42728c648f03cb04f8906411ba3da984e5 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:28:40 -0700 Subject: mtd: st_spi_fsm: fixup Kconfig dependency I hear that this driver should depend on ARCH_STI, and that "SH" is not actually a real symbol. At the same time, let's allow compile-testing on other ARCH'es. Signed-off-by: Brian Norris Acked-by: Lee Jones Reviewed-by: Marek Vasut --- drivers/mtd/devices/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index f9e32b53dfc6..9aae1042c14f 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -212,7 +212,7 @@ config MTD_DOCG3 config MTD_ST_SPI_FSM tristate "ST Microelectronics SPI FSM Serial Flash Controller" - depends on ARM || SH + depends on ARCH_STI || COMPILE_TEST help This provides an MTD device driver for the ST Microelectronics SPI Fast Sequence Mode (FSM) Serial Flash Controller and support -- cgit v1.2.3 From a402191e9b5090f1db60af7d36f2568a1e648dff Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:31:16 -0700 Subject: mtd: st_spi_fsm: kill duplicate CMD definitions These are also in serial_flash_cmds.h. (FWIW, I didn't know the C preprocessor allowed redefinitions without warning like this.) Signed-off-by: Brian Norris Acked-by: Lee Jones Reviewed-by: Marek Vasut --- drivers/mtd/devices/st_spi_fsm.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 30082a09f68b..122a8a14ef84 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -201,42 +201,6 @@ #define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */ -/* Flash Commands */ -#define FLASH_CMD_WREN 0x06 -#define FLASH_CMD_WRDI 0x04 -#define FLASH_CMD_RDID 0x9f -#define FLASH_CMD_RDSR 0x05 -#define FLASH_CMD_RDSR2 0x35 -#define FLASH_CMD_WRSR 0x01 -#define FLASH_CMD_SE_4K 0x20 -#define FLASH_CMD_SE_32K 0x52 -#define FLASH_CMD_SE 0xd8 -#define FLASH_CMD_CHIPERASE 0xc7 - -#define FLASH_CMD_READ 0x03 /* READ */ -#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */ -#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */ -#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */ -#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */ -#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */ - -#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */ -#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ -#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ -#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ -#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ - -#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */ -#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */ - -/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */ -#define FLASH_CMD_READ4 0x13 -#define FLASH_CMD_READ4_FAST 0x0c -#define FLASH_CMD_READ4_1_1_2 0x3c -#define FLASH_CMD_READ4_1_2_2 0xbc -#define FLASH_CMD_READ4_1_1_4 0x6c -#define FLASH_CMD_READ4_1_4_4 0xec - /* S25FLxxxS commands */ #define S25FL_CMD_WRITE4_1_1_4 0x34 #define S25FL_CMD_SE4 0xdc -- cgit v1.2.3 From 92d3af9ac369faf3bd2c409cf5218510500af214 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:56:06 -0700 Subject: mtd: st_spi_fsm: replace FLACH_CMD_* with SPINOR_OP_* Begin to unify the differences between serial_flash_cmds.h and spi-nor.h. Signed-off-by: Brian Norris Acked-by: Lee Jones Reviewed-by: Marek Vasut --- drivers/mtd/devices/serial_flash_cmds.h | 62 +++++++++--------- drivers/mtd/devices/st_spi_fsm.c | 112 ++++++++++++++++---------------- 2 files changed, 87 insertions(+), 87 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h index 4f0c2c7c898e..82fa1687a2d3 100644 --- a/drivers/mtd/devices/serial_flash_cmds.h +++ b/drivers/mtd/devices/serial_flash_cmds.h @@ -13,43 +13,43 @@ #define _MTD_SERIAL_FLASH_CMDS_H /* Generic Flash Commands/OPCODEs */ -#define FLASH_CMD_WREN 0x06 -#define FLASH_CMD_WRDI 0x04 -#define FLASH_CMD_RDID 0x9f -#define FLASH_CMD_RDSR 0x05 -#define FLASH_CMD_RDSR2 0x35 -#define FLASH_CMD_WRSR 0x01 -#define FLASH_CMD_SE_4K 0x20 -#define FLASH_CMD_SE_32K 0x52 -#define FLASH_CMD_SE 0xd8 -#define FLASH_CMD_CHIPERASE 0xc7 -#define FLASH_CMD_WRVCR 0x81 -#define FLASH_CMD_RDVCR 0x85 +#define SPINOR_OP_WREN 0x06 +#define SPINOR_OP_WRDI 0x04 +#define SPINOR_OP_RDID 0x9f +#define SPINOR_OP_RDSR 0x05 +#define SPINOR_OP_RDSR2 0x35 +#define SPINOR_OP_WRSR 0x01 +#define SPINOR_OP_SE_4K 0x20 +#define SPINOR_OP_SE_32K 0x52 +#define SPINOR_OP_SE 0xd8 +#define SPINOR_OP_CHIPERASE 0xc7 +#define SPINOR_OP_WRVCR 0x81 +#define SPINOR_OP_RDVCR 0x85 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ -#define FLASH_CMD_READ 0x03 /* READ */ -#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */ -#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */ -#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */ -#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */ -#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */ +#define SPINOR_OP_READ 0x03 /* READ */ +#define SPINOR_OP_READ_FAST 0x0b /* FAST READ */ +#define SPINOR_OP_READ_1_1_2 0x3b /* DUAL OUTPUT READ */ +#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */ +#define SPINOR_OP_READ_1_1_4 0x6b /* QUAD OUTPUT READ */ +#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */ -#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */ -#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ -#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ -#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ -#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ +#define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */ +#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ +#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ +#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ +#define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ -#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */ -#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */ +#define SPINOR_OP_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */ +#define SPINOR_OP_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */ /* READ commands with 32-bit addressing */ -#define FLASH_CMD_READ4 0x13 -#define FLASH_CMD_READ4_FAST 0x0c -#define FLASH_CMD_READ4_1_1_2 0x3c -#define FLASH_CMD_READ4_1_2_2 0xbc -#define FLASH_CMD_READ4_1_1_4 0x6c -#define FLASH_CMD_READ4_1_4_4 0xec +#define SPINOR_OP_READ4 0x13 +#define SPINOR_OP_READ4_FAST 0x0c +#define SPINOR_OP_READ4_1_1_2 0x3c +#define SPINOR_OP_READ4_1_2_2 0xbc +#define SPINOR_OP_READ4_1_1_4 0x6c +#define SPINOR_OP_READ4_1_4_4 0xec /* Configuration flags */ #define FLASH_FLAG_SINGLE 0x000000ff diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 122a8a14ef84..fc193a94307d 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -208,7 +208,7 @@ #define S25FL_CMD_DYBWR 0xe1 #define S25FL_CMD_DYBRD 0xe0 #define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with - * 'FLASH_CMD_WRITE_1_4_4' + * 'SPINOR_OP_WRITE_1_4_4' * as found on N25Qxxx devices! */ /* Status register */ @@ -296,7 +296,7 @@ struct flash_info { u32 jedec_id; u16 ext_id; /* - * The size listed here is what works with FLASH_CMD_SE, which isn't + * The size listed here is what works with SPINOR_OP_SE, which isn't * necessarily called a "sector" by the vendor. */ unsigned sector_size; @@ -451,22 +451,22 @@ static struct flash_info flash_types[] = { /* Default READ configurations, in order of preference */ static struct seq_rw_config default_read_configs[] = { - {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4}, - {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0}, - {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0}, - {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8}, - {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0}, + {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 2, 4}, + {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; /* Default WRITE configurations */ static struct seq_rw_config default_write_configs[] = { - {FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0}, - {FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0}, - {FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0}, - {FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0}, - {FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_WRITE, 1, 1, 1, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; @@ -489,12 +489,12 @@ static struct seq_rw_config default_write_configs[] = { * cycles. */ static struct seq_rw_config n25q_read3_configs[] = { - {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8}, - {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0}, + {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; @@ -504,12 +504,12 @@ static struct seq_rw_config n25q_read3_configs[] = { * - 'FAST' variants configured for 8 dummy cycles (see note above.) */ static struct seq_rw_config n25q_read4_configs[] = { - {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, - {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0}, + {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; @@ -522,7 +522,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) | + SEQ_OPC_OPCODE(SPINOR_OP_EN4B_ADDR) | SEQ_OPC_CSDEASSERT); seq->seq[0] = STFSM_INST_CMD1; @@ -550,12 +550,12 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) * entering a state that is incompatible with the SPIBoot Controller. */ static struct seq_rw_config stfsm_s25fl_read4_configs[] = { - {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4}, - {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0}, - {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, - {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0}, + {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4}, + {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; @@ -574,7 +574,7 @@ static struct stfsm_seq stfsm_seq_read_jedec = { .data_size = TRANSFER_SIZE(8), .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_RDID)), + SEQ_OPC_OPCODE(SPINOR_OP_RDID)), .seq = { STFSM_INST_CMD1, STFSM_INST_DATA_READ, @@ -590,7 +590,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = { .data_size = TRANSFER_SIZE(4), .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_RDSR)), + SEQ_OPC_OPCODE(SPINOR_OP_RDSR)), .seq = { STFSM_INST_CMD1, STFSM_INST_DATA_READ, @@ -606,10 +606,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = { /* 'addr_cfg' configured during initialisation */ .seq_opc = { (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_SE)), + SEQ_OPC_OPCODE(SPINOR_OP_SE)), }, .seq = { STFSM_INST_CMD1, @@ -627,10 +627,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = { static struct stfsm_seq stfsm_seq_erase_chip = { .seq_opc = { (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT), + SEQ_OPC_OPCODE(SPINOR_OP_CHIPERASE) | SEQ_OPC_CSDEASSERT), }, .seq = { STFSM_INST_CMD1, @@ -647,9 +647,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = { static struct stfsm_seq stfsm_seq_write_status = { .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WRSR)), + SEQ_OPC_OPCODE(SPINOR_OP_WRSR)), .seq = { STFSM_INST_CMD1, STFSM_INST_CMD2, @@ -665,9 +665,9 @@ static struct stfsm_seq stfsm_seq_write_status = { static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR)); + SEQ_OPC_OPCODE(SPINOR_OP_EN4B_ADDR)); seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | + SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT); seq->seq[0] = STFSM_INST_CMD2; @@ -788,7 +788,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) { struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr; - uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR; + uint32_t cmd = enter ? SPINOR_OP_EN4B_ADDR : SPINOR_OP_EX4B_ADDR; seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | @@ -812,7 +812,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) /* Use RDRS1 */ seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_RDSR)); + SEQ_OPC_OPCODE(SPINOR_OP_RDSR)); /* Load read_status sequence */ stfsm_load_seq(fsm, seq); @@ -985,7 +985,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm, if (cfg->write) seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | + SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT); /* Address configuration (24 or 32-bit addresses) */ @@ -1121,21 +1121,21 @@ static int stfsm_mx25_config(struct stfsm *fsm) } /* Check status of 'QE' bit, update if required. */ - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta, 1); + stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; if (data_pads == 4) { if (!(sta & MX25_STATUS_QE)) { /* Set 'QE' */ sta |= MX25_STATUS_QE; - stfsm_write_status(fsm, FLASH_CMD_WRSR, sta, 1, 1); + stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1); } } else { if (sta & MX25_STATUS_QE) { /* Clear 'QE' */ sta &= ~MX25_STATUS_QE; - stfsm_write_status(fsm, FLASH_CMD_WRSR, sta, 1, 1); + stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1); } } @@ -1260,7 +1260,7 @@ static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby) { struct stfsm_seq seq = { .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WREN) | + SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)), @@ -1300,7 +1300,7 @@ static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm) SEQ_OPC_CSDEASSERT), .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(FLASH_CMD_WRDI) | + SEQ_OPC_OPCODE(SPINOR_OP_WRDI) | SEQ_OPC_CSDEASSERT), .seq = { STFSM_INST_CMD1, @@ -1379,7 +1379,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm) } /* Check status of 'QE' bit, update if required. */ - stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1, 1); + stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; if (data_pads == 4) { if (!(cr1 & STFSM_S25FL_CONFIG_QE)) { @@ -1397,9 +1397,9 @@ static int stfsm_s25fl_config(struct stfsm *fsm) } } if (update_sr) { - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1, 1); + stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1); sta_wr = ((uint16_t)cr1 << 8) | sr1; - stfsm_write_status(fsm, FLASH_CMD_WRSR, sta_wr, 2, 1); + stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1); } /* @@ -1424,7 +1424,7 @@ static int stfsm_w25q_config(struct stfsm *fsm) return ret; /* Check status of 'QE' bit, update if required. */ - stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sr2, 1); + stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1); data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; if (data_pads == 4) { if (!(sr2 & W25Q_STATUS_QE)) { @@ -1441,9 +1441,9 @@ static int stfsm_w25q_config(struct stfsm *fsm) } if (update_sr) { /* Write status register */ - stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1, 1); + stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1); sr_wr = ((uint16_t)sr2 << 8) | sr1; - stfsm_write_status(fsm, FLASH_CMD_WRSR, sr_wr, 2, 1); + stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1); } return 0; -- cgit v1.2.3 From 6c8e1b33aac94c1923b1b5acc54644094a9b6a78 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 19:02:14 -0700 Subject: mtd: st_spi_fsm: begin using spi-nor.h opcodes Many of the serial_flash_cmds.h opcodes are duplicated with spi-nor.h. Let's begin to unify them. Signed-off-by: Brian Norris Acked-by: Lee Jones Reviewed-by: Marek Vasut --- drivers/mtd/devices/serial_flash_cmds.h | 20 -------------------- drivers/mtd/devices/st_spi_fsm.c | 9 +++++---- 2 files changed, 5 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h index 82fa1687a2d3..f59a125295d0 100644 --- a/drivers/mtd/devices/serial_flash_cmds.h +++ b/drivers/mtd/devices/serial_flash_cmds.h @@ -13,25 +13,12 @@ #define _MTD_SERIAL_FLASH_CMDS_H /* Generic Flash Commands/OPCODEs */ -#define SPINOR_OP_WREN 0x06 -#define SPINOR_OP_WRDI 0x04 -#define SPINOR_OP_RDID 0x9f -#define SPINOR_OP_RDSR 0x05 #define SPINOR_OP_RDSR2 0x35 -#define SPINOR_OP_WRSR 0x01 -#define SPINOR_OP_SE_4K 0x20 -#define SPINOR_OP_SE_32K 0x52 -#define SPINOR_OP_SE 0xd8 -#define SPINOR_OP_CHIPERASE 0xc7 #define SPINOR_OP_WRVCR 0x81 #define SPINOR_OP_RDVCR 0x85 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ -#define SPINOR_OP_READ 0x03 /* READ */ -#define SPINOR_OP_READ_FAST 0x0b /* FAST READ */ -#define SPINOR_OP_READ_1_1_2 0x3b /* DUAL OUTPUT READ */ #define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */ -#define SPINOR_OP_READ_1_1_4 0x6b /* QUAD OUTPUT READ */ #define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */ #define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */ @@ -40,15 +27,8 @@ #define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ -#define SPINOR_OP_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */ -#define SPINOR_OP_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */ - /* READ commands with 32-bit addressing */ -#define SPINOR_OP_READ4 0x13 -#define SPINOR_OP_READ4_FAST 0x0c -#define SPINOR_OP_READ4_1_1_2 0x3c #define SPINOR_OP_READ4_1_2_2 0xbc -#define SPINOR_OP_READ4_1_1_4 0x6c #define SPINOR_OP_READ4_1_4_4 0xec /* Configuration flags */ diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index fc193a94307d..7cc49ba78f68 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -522,7 +523,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(SPINOR_OP_EN4B_ADDR) | + SEQ_OPC_OPCODE(SPINOR_OP_EN4B) | SEQ_OPC_CSDEASSERT); seq->seq[0] = STFSM_INST_CMD1; @@ -630,7 +631,7 @@ static struct stfsm_seq stfsm_seq_erase_chip = { SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(SPINOR_OP_CHIPERASE) | SEQ_OPC_CSDEASSERT), + SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT), }, .seq = { STFSM_INST_CMD1, @@ -665,7 +666,7 @@ static struct stfsm_seq stfsm_seq_write_status = { static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | - SEQ_OPC_OPCODE(SPINOR_OP_EN4B_ADDR)); + SEQ_OPC_OPCODE(SPINOR_OP_EN4B)); seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT); @@ -788,7 +789,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) { struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr; - uint32_t cmd = enter ? SPINOR_OP_EN4B_ADDR : SPINOR_OP_EX4B_ADDR; + uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | -- cgit v1.2.3 From 8ac326fce996893cadc0f161207f4069fdbaa2cd Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 10 Apr 2014 11:05:34 -0700 Subject: mtd: spi-nor: allow to be built as module There's no reason this can't be a module. Also, give SPI-NOR its own submenu. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut --- drivers/mtd/spi-nor/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index d682fb440e06..f8acfa4310ef 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,5 +1,5 @@ -config MTD_SPI_NOR - bool "SPI-NOR device support" +menuconfig MTD_SPI_NOR + tristate "SPI-NOR device support" depends on MTD help This is the framework for the SPI NOR which can be used by the SPI -- cgit v1.2.3 From a965d04c977096ff91f12f96eb7b35b7b03af48c Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 10 Apr 2014 15:49:38 -0700 Subject: mtd: fsl-quadspi: fix __iomem annotations This corrects some sparse warnings: drivers/mtd/spi-nor/fsl-quadspi.c:281:31: warning: incorrect type in initializer (different address spaces) [sparse] drivers/mtd/spi-nor/fsl-quadspi.c:281:31: expected void *[noderef] base [sparse] drivers/mtd/spi-nor/fsl-quadspi.c:281:31: got void [noderef] *iobase [sparse] (etc.) Signed-off-by: Brian Norris Acked-by: Huang Shijie --- drivers/mtd/spi-nor/fsl-quadspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index b41bbbc531ff..8d659a2888d5 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -278,7 +278,7 @@ static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) static void fsl_qspi_init_lut(struct fsl_qspi *q) { - void *__iomem base = q->iobase; + void __iomem *base = q->iobase; int rxfifo = q->devtype_data->rxfifo; u32 lut_base; u8 cmd, addrlen, dummy; @@ -422,7 +422,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) static int fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) { - void *__iomem base = q->iobase; + void __iomem *base = q->iobase; int seqid; u32 reg, reg2; int err; -- cgit v1.2.3 From 38e2eee9abf202b5edad73eb0288e0a4dfaacfca Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 11 Apr 2014 12:15:32 -0700 Subject: mtd: st_spi_fsm: correct type issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compile-testing for a 64-bit arch uncovers several bad casts: In file included from include/linux/linkage.h:4:0, from include/linux/kernel.h:6, from drivers/mtd/devices/st_spi_fsm.c:15: drivers/mtd/devices/st_spi_fsm.c: In function ‘stfsm_read_fifo’: drivers/mtd/devices/st_spi_fsm.c:758:11: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); ... Use uintptr_t instead of uint32_t, since it's guaranteed to be pointer-sized. We also see this warning, if size_t is not 32 bits wide: In file included from drivers/mtd/devices/st_spi_fsm.c:15:0: drivers/mtd/devices/st_spi_fsm.c: In function ‘stfsm_mtd_write’: include/linux/kernel.h:712:17: warning: comparison of distinct pointer types lacks a cast [enabled by default] (void) (&_min1 == &_min2); \ ^ drivers/mtd/devices/st_spi_fsm.c:1704:11: note: in expansion of macro ‘min’ bytes = min(FLASH_PAGESIZE - page_offs, len); ^ Just use min_t() to force the type conversion, since we don't really want to upgrade 'page_offs' and 'bytes' to size_t; they only should be handling <= 256 byte offsets. Signed-off-by: Brian Norris Acked-by: Lee Jones --- drivers/mtd/devices/st_spi_fsm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 7cc49ba78f68..f97fe144077d 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -755,7 +755,7 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size); - BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); + BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3)); while (remaining) { for (;;) { @@ -779,7 +779,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size); - BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); + BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3)); writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); @@ -1474,7 +1474,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, read_mask = (data_pads << 2) - 1; /* Handle non-aligned buf */ - p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf; + p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf; /* Handle non-aligned size */ size_ub = (size + read_mask) & ~read_mask; @@ -1496,7 +1496,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, } /* Handle non-aligned buf */ - if ((uint32_t)buf & 0x3) + if ((uintptr_t)buf & 0x3) memcpy(buf, page_buf, size); /* Wait for sequence to finish */ @@ -1538,7 +1538,7 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, write_mask = (data_pads << 2) - 1; /* Handle non-aligned buf */ - if ((uint32_t)buf & 0x3) { + if ((uintptr_t)buf & 0x3) { memcpy(page_buf, buf, size); p = (uint8_t *)page_buf; } else { @@ -1701,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, while (len) { /* Write up to page boundary */ - bytes = min(FLASH_PAGESIZE - page_offs, len); + bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len); ret = stfsm_write(fsm, b, bytes, to); if (ret) -- cgit v1.2.3 From dc002f99f3aa7c05981ecbbbc20efe5e8befe98a Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 16 Apr 2014 01:40:17 -0700 Subject: mtd: st_spi_fsm: only build for ARM COMPILE_TEST allows us to build this driver on other arch'es. But not all arch'es have the right I/O accessors -- particularly, x86 is missing readsl() and writesl(). So just restrict this driver to ARCH_STI. It's still buildable for a multiplatform ARM kernel, so it can get decent compile coverage. Signed-off-by: Brian Norris Acked-by: Lee Jones --- drivers/mtd/devices/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 9aae1042c14f..c49d0b127fef 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -212,7 +212,7 @@ config MTD_DOCG3 config MTD_ST_SPI_FSM tristate "ST Microelectronics SPI FSM Serial Flash Controller" - depends on ARCH_STI || COMPILE_TEST + depends on ARCH_STI help This provides an MTD device driver for the ST Microelectronics SPI Fast Sequence Mode (FSM) Serial Flash Controller and support -- cgit v1.2.3