From d105c51a428a72a9af42759c472df9960c496d67 Mon Sep 17 00:00:00 2001 From: rmilecki Date: Wed, 17 Dec 2014 14:53:25 +0000 Subject: [PATCH] bcm53xx: backport spi-nor changes and update bcm53xxspiflash MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki git-svn-id: svn://svn.openwrt.org/openwrt/trunk@43738 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../files/drivers/mtd/spi-nor/bcm53xxspiflash.c | 227 +++++++ .../patches-3.14/002-mtd-spi-nor-from-3.18.patch | 110 +++- .../patches-3.14/003-mtd-spi-nor-from-3.19.patch | 662 +++++++++++++++++++++ ...-allow-NULL-as-spi_device_id-in-spi_nor_s.patch | 46 -- .../403-mtd-spi-nor-refactor-wait-till-ready.patch | 374 ------------ ...spiflash-new-driver-for-SPI-flahes-on-Bro.patch | 244 -------- ...spiflash-try-using-JEDEC-as-one-of-method.patch | 42 -- .../patches-3.18/003-mtd-spi-nor-from-3.19.patch | 662 +++++++++++++++++++++ ...-allow-NULL-as-spi_device_id-in-spi_nor_s.patch | 46 -- .../403-mtd-spi-nor-refactor-wait-till-ready.patch | 374 ------------ ...spiflash-new-driver-for-SPI-flahes-on-Bro.patch | 244 -------- ...spiflash-try-using-JEDEC-as-one-of-method.patch | 42 -- 12 files changed, 1651 insertions(+), 1422 deletions(-) create mode 100644 target/linux/bcm53xx/files/drivers/mtd/spi-nor/bcm53xxspiflash.c create mode 100644 target/linux/bcm53xx/patches-3.14/003-mtd-spi-nor-from-3.19.patch delete mode 100644 target/linux/bcm53xx/patches-3.14/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch delete mode 100644 target/linux/bcm53xx/patches-3.14/403-mtd-spi-nor-refactor-wait-till-ready.patch delete mode 100644 target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch create mode 100644 target/linux/bcm53xx/patches-3.18/003-mtd-spi-nor-from-3.19.patch delete mode 100644 target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch delete mode 100644 target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch delete mode 100644 target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch diff --git a/target/linux/bcm53xx/files/drivers/mtd/spi-nor/bcm53xxspiflash.c b/target/linux/bcm53xx/files/drivers/mtd/spi-nor/bcm53xxspiflash.c new file mode 100644 index 0000000000..f192f4e59b --- /dev/null +++ b/target/linux/bcm53xx/files/drivers/mtd/spi-nor/bcm53xxspiflash.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include + +static const char * const probes[] = { "bcm47xxpart", NULL }; + +struct bcm53xxsf { + struct spi_device *spi; + struct mtd_info mtd; + struct spi_nor nor; +}; + +/************************************************** + * spi-nor API + **************************************************/ + +static int bcm53xxspiflash_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len) +{ + struct bcm53xxsf *b53sf = nor->priv; + + return spi_write_then_read(b53sf->spi, &opcode, 1, buf, len); +} + +static int bcm53xxspiflash_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len, int write_enable) +{ + struct bcm53xxsf *b53sf = nor->priv; + u8 *cmd = kzalloc(len + 1, GFP_KERNEL); + int err; + + if (!cmd) + return -ENOMEM; + + cmd[0] = opcode; + memcpy(&cmd[1], buf, len); + err = spi_write(b53sf->spi, cmd, len + 1); + + kfree(cmd); + + return err; +} + +static int bcm53xxspiflash_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct bcm53xxsf *b53sf = nor->priv; + struct spi_message m; + struct spi_transfer t[2] = { { 0 }, { 0 } }; + unsigned char cmd[5]; + int cmd_len = 0; + int err; + + spi_message_init(&m); + + cmd[cmd_len++] = SPINOR_OP_READ; + if (b53sf->mtd.size > 0x1000000) + cmd[cmd_len++] = (from & 0xFF000000) >> 24; + cmd[cmd_len++] = (from & 0x00FF0000) >> 16; + cmd[cmd_len++] = (from & 0x0000FF00) >> 8; + cmd[cmd_len++] = (from & 0x000000FF) >> 0; + + t[0].tx_buf = cmd; + t[0].len = cmd_len; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + err = spi_sync(b53sf->spi, &m); + if (err) + return err; + + if (retlen && m.actual_length > cmd_len) + *retlen = m.actual_length - cmd_len; + + return 0; +} + +static void bcm53xxspiflash_write(struct spi_nor *nor, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct bcm53xxsf *b53sf = nor->priv; + struct spi_message m; + struct spi_transfer t = { 0 }; + u8 *cmd = kzalloc(len + 5, GFP_KERNEL); + int cmd_len = 0; + int err; + + if (!cmd) + return; + + spi_message_init(&m); + + cmd[cmd_len++] = nor->program_opcode; + if (b53sf->mtd.size > 0x1000000) + cmd[cmd_len++] = (to & 0xFF000000) >> 24; + cmd[cmd_len++] = (to & 0x00FF0000) >> 16; + cmd[cmd_len++] = (to & 0x0000FF00) >> 8; + cmd[cmd_len++] = (to & 0x000000FF) >> 0; + memcpy(&cmd[cmd_len], buf, len); + + t.tx_buf = cmd; + t.len = cmd_len + len; + spi_message_add_tail(&t, &m); + + err = spi_sync(b53sf->spi, &m); + if (err) + goto out; + + if (retlen && m.actual_length > cmd_len) + *retlen += m.actual_length - cmd_len; + +out: + kfree(cmd); +} + +static int bcm53xxspiflash_erase(struct spi_nor *nor, loff_t offs) +{ + struct bcm53xxsf *b53sf = nor->priv; + unsigned char cmd[5]; + int i; + + i = 0; + cmd[i++] = nor->erase_opcode; + if (b53sf->mtd.size > 0x1000000) + cmd[i++] = (offs & 0xFF000000) >> 24; + cmd[i++] = ((offs & 0x00FF0000) >> 16); + cmd[i++] = ((offs & 0x0000FF00) >> 8); + cmd[i++] = ((offs & 0x000000FF) >> 0); + + return spi_write(b53sf->spi, cmd, i); +} + +static const char *bcm53xxspiflash_chip_name(struct spi_nor *nor) +{ + struct bcm53xxsf *b53sf = nor->priv; + struct device *dev = &b53sf->spi->dev; + unsigned char cmd[4]; + unsigned char resp[2]; + int err; + + /* SST and Winbond/NexFlash specific command */ + cmd[0] = 0x90; /* Read Manufacturer / Device ID */ + cmd[1] = 0; + cmd[2] = 0; + cmd[3] = 0; + err = spi_write_then_read(b53sf->spi, cmd, 4, resp, 2); + if (err < 0) { + dev_err(dev, "error reading SPI flash id\n"); + return ERR_PTR(-EBUSY); + } + switch (resp[0]) { + case 0xef: /* Winbond/NexFlash */ + switch (resp[1]) { + case 0x17: + return "w25q128"; + } + dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n", + resp[0], resp[1]); + return NULL; + } + + /* TODO: Try more ID commands */ + + return NULL; +} + +/************************************************** + * SPI driver + **************************************************/ + +static int bcm53xxspiflash_probe(struct spi_device *spi) +{ + struct bcm53xxsf *b53sf; + struct spi_nor *nor; + int err; + + b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL); + if (!b53sf) + return -ENOMEM; + spi_set_drvdata(spi, b53sf); + + nor = &b53sf->nor; + b53sf->spi = spi; + b53sf->mtd.priv = &b53sf->nor; + + nor->mtd = &b53sf->mtd; + nor->dev = &spi->dev; + nor->read_reg = bcm53xxspiflash_read_reg; + nor->write_reg = bcm53xxspiflash_write_reg; + nor->read = bcm53xxspiflash_read; + nor->write = bcm53xxspiflash_write; + nor->erase = bcm53xxspiflash_erase; + nor->priv = b53sf; + + err = spi_nor_scan(&b53sf->nor, bcm53xxspiflash_chip_name(nor), + SPI_NOR_NORMAL); + if (err) + return err; + + err = mtd_device_parse_register(&b53sf->mtd, probes, NULL, NULL, 0); + if (err) + return err; + + return 0; +} + +static int bcm53xxspiflash_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver bcm53xxspiflash_driver = { + .driver = { + .name = "bcm53xxspiflash", + .owner = THIS_MODULE, + }, + .probe = bcm53xxspiflash_probe, + .remove = bcm53xxspiflash_remove, +}; + +module_spi_driver(bcm53xxspiflash_driver); diff --git a/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-from-3.18.patch b/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-from-3.18.patch index 6fd5c89fce..873861ab63 100644 --- a/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-from-3.18.patch +++ b/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-from-3.18.patch @@ -1,6 +1,24 @@ --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -611,6 +611,7 @@ const struct spi_device_id spi_nor_ids[] +@@ -28,6 +28,8 @@ + + #define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) + ++static const struct spi_device_id *spi_nor_match_id(const char *name); ++ + /* + * Read the status register, returning its value in the location + * Return the status register value. +@@ -473,7 +475,7 @@ struct flash_info { + * 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[] = { ++static 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) }, +@@ -611,6 +613,7 @@ const struct spi_device_id spi_nor_ids[] { "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) }, @@ -8,7 +26,7 @@ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, -@@ -623,7 +624,6 @@ const struct spi_device_id spi_nor_ids[] +@@ -623,7 +626,6 @@ const struct spi_device_id spi_nor_ids[] { "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) }, @@ -16,7 +34,15 @@ { "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) }, -@@ -671,11 +671,6 @@ static const struct spi_device_id *spi_n +@@ -637,7 +639,6 @@ 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) + { +@@ -671,11 +672,6 @@ static const struct spi_device_id *spi_n return ERR_PTR(-ENODEV); } @@ -28,15 +54,21 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { -@@ -920,7 +915,6 @@ int spi_nor_scan(struct spi_nor *nor, co - enum read_mode mode) +@@ -916,11 +912,10 @@ static int spi_nor_check(struct spi_nor + return 0; + } + +-int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, +- enum read_mode mode) ++int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) { ++ const struct spi_device_id *id = NULL; 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; -@@ -931,34 +925,12 @@ int spi_nor_scan(struct spi_nor *nor, co +@@ -931,34 +926,16 @@ int spi_nor_scan(struct spi_nor *nor, co if (ret) return ret; @@ -61,7 +93,10 @@ - else - dev_warn(dev, "unrecognized id %s\n", data->type); - } -- ++ id = spi_nor_match_id(name); ++ if (!id) ++ return -ENOENT; + info = (void *)id->driver_data; if (info->jedec_id) { @@ -72,7 +107,7 @@ if (IS_ERR(jid)) { return PTR_ERR(jid); } else if (jid != id) { -@@ -990,11 +962,8 @@ int spi_nor_scan(struct spi_nor *nor, co +@@ -990,11 +967,8 @@ int spi_nor_scan(struct spi_nor *nor, co write_sr(nor, 0); } @@ -85,7 +120,7 @@ mtd->type = MTD_NORFLASH; mtd->writesize = 1; mtd->flags = MTD_CAP_NORFLASH; -@@ -1018,6 +987,7 @@ int spi_nor_scan(struct spi_nor *nor, co +@@ -1018,6 +992,7 @@ int spi_nor_scan(struct spi_nor *nor, co nor->wait_till_ready == spi_nor_wait_till_ready) nor->wait_till_ready = spi_nor_wait_till_fsr_ready; @@ -93,7 +128,7 @@ /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { nor->erase_opcode = SPINOR_OP_BE_4K; -@@ -1025,7 +995,9 @@ int spi_nor_scan(struct spi_nor *nor, co +@@ -1025,7 +1000,9 @@ int spi_nor_scan(struct spi_nor *nor, co } else if (info->flags & SECT_4K_PMC) { nor->erase_opcode = SPINOR_OP_BE_4K_PMC; mtd->erasesize = 4096; @@ -104,6 +139,23 @@ nor->erase_opcode = SPINOR_OP_SE; mtd->erasesize = info->sector_size; } +@@ -1141,7 +1118,7 @@ int spi_nor_scan(struct spi_nor *nor, co + } + EXPORT_SYMBOL_GPL(spi_nor_scan); + +-const struct spi_device_id *spi_nor_match_id(char *name) ++static const struct spi_device_id *spi_nor_match_id(const char *name) + { + const struct spi_device_id *id = spi_nor_ids; + +@@ -1152,7 +1129,6 @@ const struct spi_device_id *spi_nor_matc + } + return NULL; + } +-EXPORT_SYMBOL_GPL(spi_nor_match_id); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Huang Shijie "); --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -7,6 +7,20 @@ menuconfig MTD_SPI_NOR @@ -127,3 +179,41 @@ config SPI_FSL_QUADSPI tristate "Freescale Quad SPI controller" depends on ARCH_MXC +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -187,32 +187,17 @@ struct spi_nor { + /** + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure +- * @id: the spi_device_id provided by the driver ++ * @name: the chip type name + * @mode: the read mode supported by the driver + * + * The drivers can use this fuction to scan the SPI NOR. + * In the scanning, it will try to get all the necessary information to + * fill the mtd_info{} and the spi_nor{}. + * +- * The board may assigns a spi_device_id with @id which be used to compared with +- * the spi_device_id detected by the scanning. ++ * The chip type name can be provided through the @name parameter. + * + * Return: 0 for success, others for failure. + */ +-int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, +- enum read_mode mode); +-extern const struct spi_device_id spi_nor_ids[]; +- +-/** +- * spi_nor_match_id() - find the spi_device_id by the name +- * @name: the name of the spi_device_id +- * +- * The drivers use this function to find the spi_device_id +- * specified by the @name. +- * +- * Return: returns the right spi_device_id pointer on success, +- * and returns NULL on failure. +- */ +-const struct spi_device_id *spi_nor_match_id(char *name); ++int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode); + + #endif diff --git a/target/linux/bcm53xx/patches-3.14/003-mtd-spi-nor-from-3.19.patch b/target/linux/bcm53xx/patches-3.14/003-mtd-spi-nor-from-3.19.patch new file mode 100644 index 0000000000..5451b9c6a4 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/003-mtd-spi-nor-from-3.19.patch @@ -0,0 +1,662 @@ +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -26,7 +26,38 @@ + /* 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) ++#define SPI_NOR_MAX_ID_LEN 6 ++ ++struct flash_info { ++ /* ++ * This array stores the ID bytes. ++ * The first three bytes are the JEDIC ID. ++ * JEDEC ID zero means "no ID" (mostly older chips). ++ */ ++ u8 id[SPI_NOR_MAX_ID_LEN]; ++ u8 id_len; ++ ++ /* The size listed here is what works with SPINOR_OP_SE, which isn't ++ * necessarily called a "sector" by the vendor. ++ */ ++ unsigned sector_size; ++ u16 n_sectors; ++ ++ u16 page_size; ++ u16 addr_width; ++ ++ u16 flags; ++#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 /* 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 */ ++#define USE_FSR 0x80 /* use flag status register */ ++}; ++ ++#define JEDEC_MFR(info) ((info)->id[0]) + + static const struct spi_device_id *spi_nor_match_id(const char *name); + +@@ -98,7 +129,7 @@ static inline int spi_nor_read_dummy_cyc + case SPI_NOR_FAST: + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: +- return 1; ++ return 8; + case SPI_NOR_NORMAL: + return 0; + } +@@ -138,13 +169,14 @@ static inline struct spi_nor *mtd_to_spi + } + + /* Enable/disable 4-byte addressing mode. */ +-static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) ++static inline int set_4byte(struct spi_nor *nor, struct flash_info *info, ++ int enable) + { + int status; + bool need_wren = false; + u8 cmd; + +- switch (JEDEC_MFR(jedec_id)) { ++ switch (JEDEC_MFR(info)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; +@@ -165,81 +197,74 @@ static inline int set_4byte(struct spi_n + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); + } + } +- +-static int spi_nor_wait_till_ready(struct spi_nor *nor) ++static inline int spi_nor_sr_ready(struct spi_nor *nor) + { +- unsigned long deadline; +- int sr; +- +- deadline = jiffies + MAX_READY_WAIT_JIFFIES; +- +- do { +- cond_resched(); ++ int sr = read_sr(nor); ++ if (sr < 0) ++ return sr; ++ else ++ return !(sr & SR_WIP); ++} + +- sr = read_sr(nor); +- if (sr < 0) +- break; +- else if (!(sr & SR_WIP)) +- return 0; +- } while (!time_after_eq(jiffies, deadline)); ++static inline int spi_nor_fsr_ready(struct spi_nor *nor) ++{ ++ int fsr = read_fsr(nor); ++ if (fsr < 0) ++ return fsr; ++ else ++ return fsr & FSR_READY; ++} + +- return -ETIMEDOUT; ++static int spi_nor_ready(struct spi_nor *nor) ++{ ++ int sr, fsr; ++ sr = spi_nor_sr_ready(nor); ++ if (sr < 0) ++ return sr; ++ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; ++ if (fsr < 0) ++ return fsr; ++ return sr && fsr; + } + +-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) ++/* ++ * Service routine to read status register until ready, or timeout occurs. ++ * Returns non-zero if error. ++ */ ++static int spi_nor_wait_till_ready(struct spi_nor *nor) + { + unsigned long deadline; +- int sr; +- int fsr; ++ int timeout = 0, ret; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + +- do { ++ while (!timeout) { ++ if (time_after_eq(jiffies, deadline)) ++ timeout = 1; ++ ++ ret = spi_nor_ready(nor); ++ if (ret < 0) ++ return ret; ++ if (ret) ++ return 0; ++ + cond_resched(); ++ } + +- sr = read_sr(nor); +- if (sr < 0) { +- break; +- } else if (!(sr & SR_WIP)) { +- fsr = read_fsr(nor); +- if (fsr < 0) +- break; +- if (fsr & FSR_READY) +- return 0; +- } +- } while (!time_after_eq(jiffies, deadline)); ++ dev_err(nor->dev, "flash operation timed out\n"); + + 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, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); + } + +@@ -294,11 +319,17 @@ static int spi_nor_erase(struct mtd_info + + /* whole-chip erase? */ + if (len == mtd->size) { ++ write_enable(nor); ++ + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto erase_err; ++ + /* REVISIT in some cases we could speed up erasing large regions + * 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. +@@ -307,6 +338,8 @@ static int spi_nor_erase(struct mtd_info + /* "sector"-at-a-time erase */ + } else { + while (len) { ++ write_enable(nor); ++ + if (nor->erase(nor, addr)) { + ret = -EIO; + goto erase_err; +@@ -314,9 +347,15 @@ static int spi_nor_erase(struct mtd_info + + addr += mtd->erasesize; + len -= mtd->erasesize; ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto erase_err; + } + } + ++ write_disable(nor); ++ + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + + instr->state = MTD_ERASE_DONE; +@@ -341,11 +380,6 @@ static int spi_nor_lock(struct mtd_info + 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)) +@@ -388,11 +422,6 @@ static int spi_nor_unlock(struct mtd_inf + 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)) +@@ -424,38 +453,34 @@ err: + 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 SPINOR_OP_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 /* 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 /* 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 */ +-#define USE_FSR 0x80 /* use flag status register */ +-}; +- ++/* Used when the "_ext_id" is two bytes at most */ + #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), \ ++ .id = { \ ++ ((_jedec_id) >> 16) & 0xff, \ ++ ((_jedec_id) >> 8) & 0xff, \ ++ (_jedec_id) & 0xff, \ ++ ((_ext_id) >> 8) & 0xff, \ ++ (_ext_id) & 0xff, \ ++ }, \ ++ .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ ++ .sector_size = (_sector_size), \ ++ .n_sectors = (_n_sectors), \ ++ .page_size = 256, \ ++ .flags = (_flags), \ ++ }) ++ ++#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ ++ ((kernel_ulong_t)&(struct flash_info) { \ ++ .id = { \ ++ ((_jedec_id) >> 16) & 0xff, \ ++ ((_jedec_id) >> 8) & 0xff, \ ++ (_jedec_id) & 0xff, \ ++ ((_ext_id) >> 16) & 0xff, \ ++ ((_ext_id) >> 8) & 0xff, \ ++ (_ext_id) & 0xff, \ ++ }, \ ++ .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ +@@ -507,6 +532,9 @@ static const struct spi_device_id spi_no + { "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) }, + ++ /* Fujitsu */ ++ { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, ++ + /* GigaDevice */ + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, +@@ -532,6 +560,7 @@ static const struct spi_device_id spi_no + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + + /* Micron */ ++ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, +@@ -556,6 +585,7 @@ static const struct spi_device_id spi_no + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, ++ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, +@@ -577,6 +607,7 @@ static const struct spi_device_id spi_no + { "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) }, ++ { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, +@@ -588,7 +619,6 @@ static const struct spi_device_id spi_no + { "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) }, +@@ -643,32 +673,24 @@ static const struct spi_device_id spi_no + static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) + { + int tmp; +- u8 id[5]; +- u32 jedec; +- u16 ext_jedec; ++ u8 id[SPI_NOR_MAX_ID_LEN]; + struct flash_info *info; + +- tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); ++ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + 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) ++ if (info->id_len) { ++ if (!memcmp(info->id, id, info->id_len)) + return &spi_nor_ids[tmp]; + } + } +- dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); ++ dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n", ++ id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); + } + +@@ -703,11 +725,6 @@ static int sst_write(struct mtd_info *mt + 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; +@@ -719,7 +736,7 @@ static int sst_write(struct mtd_info *mt + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + } +@@ -731,7 +748,7 @@ static int sst_write(struct mtd_info *mt + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; +@@ -740,7 +757,7 @@ static int sst_write(struct mtd_info *mt + nor->sst_write_second = false; + + write_disable(nor); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + +@@ -751,7 +768,7 @@ static int sst_write(struct mtd_info *mt + nor->program_opcode = SPINOR_OP_BP; + nor->write(nor, to, 1, retlen, buf + actual); + +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); +@@ -779,11 +796,6 @@ static int spi_nor_write(struct mtd_info + 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); +@@ -802,16 +814,20 @@ static int spi_nor_write(struct mtd_info + if (page_size > nor->page_size) + page_size = nor->page_size; + +- wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto write_err; ++ + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + ++ ret = spi_nor_wait_till_ready(nor); + write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); +- return 0; ++ return ret; + } + + static int macronix_quad_enable(struct spi_nor *nor) +@@ -824,7 +840,7 @@ static int macronix_quad_enable(struct s + nor->cmd_buf[0] = val | SR_QUAD_EN_MX; + nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); + +- if (wait_till_ready(nor)) ++ if (spi_nor_wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); +@@ -874,11 +890,11 @@ static int spansion_quad_enable(struct s + return 0; + } + +-static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) ++static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) + { + int status; + +- switch (JEDEC_MFR(jedec_id)) { ++ switch (JEDEC_MFR(info)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { +@@ -904,11 +920,6 @@ static int spi_nor_check(struct spi_nor + 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; + } + +@@ -926,16 +937,24 @@ int spi_nor_scan(struct spi_nor *nor, co + if (ret) + return ret; + +- id = spi_nor_match_id(name); +- if (!id) ++ /* Try to auto-detect if chip name wasn't specified */ ++ if (!name) ++ id = spi_nor_read_id(nor); ++ else ++ id = spi_nor_match_id(name); ++ if (IS_ERR_OR_NULL(id)) + return -ENOENT; + + info = (void *)id->driver_data; + +- if (info->jedec_id) { ++ /* ++ * If caller has specified name of flash model that can normally be ++ * detected using JEDEC, let's verify it. ++ */ ++ if (name && info->id_len) { + const struct spi_device_id *jid; + +- jid = nor->read_id(nor); ++ jid = spi_nor_read_id(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { +@@ -960,9 +979,9 @@ int spi_nor_scan(struct spi_nor *nor, co + * 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) { ++ if (JEDEC_MFR(info) == CFI_MFR_ATMEL || ++ JEDEC_MFR(info) == CFI_MFR_INTEL || ++ JEDEC_MFR(info) == CFI_MFR_SST) { + write_enable(nor); + write_sr(nor, 0); + } +@@ -977,7 +996,7 @@ int spi_nor_scan(struct spi_nor *nor, co + mtd->_read = spi_nor_read; + + /* nor protection support for STmicro chips */ +- if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { ++ if (JEDEC_MFR(info) == CFI_MFR_ST) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + } +@@ -988,9 +1007,8 @@ int spi_nor_scan(struct spi_nor *nor, co + else + mtd->_write = spi_nor_write; + +- if ((info->flags & USE_FSR) && +- nor->wait_till_ready == spi_nor_wait_till_ready) +- nor->wait_till_ready = spi_nor_wait_till_fsr_ready; ++ if (info->flags & USE_FSR) ++ nor->flags |= SNOR_F_USE_FSR; + + #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ +@@ -1031,7 +1049,7 @@ int spi_nor_scan(struct spi_nor *nor, co + + /* 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); ++ ret = set_quad_mode(nor, info); + if (ret) { + dev_err(dev, "quad mode not supported\n"); + return ret; +@@ -1067,7 +1085,7 @@ int spi_nor_scan(struct spi_nor *nor, co + 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) { ++ if (JEDEC_MFR(info) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: +@@ -1088,7 +1106,7 @@ int spi_nor_scan(struct spi_nor *nor, co + nor->erase_opcode = SPINOR_OP_SE_4B; + mtd->erasesize = info->sector_size; + } else +- set_4byte(nor, info->jedec_id, 1); ++ set_4byte(nor, info, 1); + } else { + nor->addr_width = 3; + } +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -116,6 +116,10 @@ enum spi_nor_ops { + SPI_NOR_OPS_UNLOCK, + }; + ++enum spi_nor_option_flags { ++ SNOR_F_USE_FSR = BIT(0), ++}; ++ + /** + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure +@@ -129,6 +133,7 @@ enum spi_nor_ops { + * @program_opcode: the program opcode + * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation ++ * @flags: flag options for the current SPI-NOR (SNOR_F_*) + * @cfg: used by the read_xfer/write_xfer + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the +@@ -139,9 +144,6 @@ enum spi_nor_ops { + * @write_xfer: [OPTIONAL] the writefundamental primitive + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register +- * @read_id: [REPLACEABLE] read out the ID data, and find +- * the proper spi_device_id +- * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR +@@ -160,6 +162,7 @@ struct spi_nor { + u8 program_opcode; + enum read_mode flash_read; + bool sst_write_second; ++ u32 flags; + struct spi_nor_xfer_cfg cfg; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + +@@ -172,8 +175,6 @@ struct spi_nor { + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable); +- const struct spi_device_id *(*read_id)(struct spi_nor *nor); +- int (*wait_till_ready)(struct spi_nor *nor); + + int (*read)(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *read_buf); diff --git a/target/linux/bcm53xx/patches-3.14/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch b/target/linux/bcm53xx/patches-3.14/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch deleted file mode 100644 index 6cc77528e0..0000000000 --- a/target/linux/bcm53xx/patches-3.14/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- a/drivers/mtd/spi-nor/spi-nor.c -+++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -925,29 +925,23 @@ int spi_nor_scan(struct spi_nor *nor, co - if (ret) - return ret; - -- info = (void *)id->driver_data; -- -- if (info->jedec_id) { -- const struct spi_device_id *jid; -- -- jid = nor->read_id(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; -+ if (id) { -+ info = (void *)id->driver_data; -+ if (info->jedec_id) { -+ dev_warn(dev, -+ "passed SPI device ID (%s) contains JEDEC, ignoring it, driver should be fixed!\n", -+ id->name); -+ id = NULL; - } - } - -+ if (!id) { -+ id = nor->read_id(nor); -+ if (IS_ERR(id)) -+ return PTR_ERR(id); -+ } -+ info = (void *)id->driver_data; -+ - mutex_init(&nor->lock); - - /* diff --git a/target/linux/bcm53xx/patches-3.14/403-mtd-spi-nor-refactor-wait-till-ready.patch b/target/linux/bcm53xx/patches-3.14/403-mtd-spi-nor-refactor-wait-till-ready.patch deleted file mode 100644 index 79a5131314..0000000000 --- a/target/linux/bcm53xx/patches-3.14/403-mtd-spi-nor-refactor-wait-till-ready.patch +++ /dev/null @@ -1,374 +0,0 @@ ---- a/drivers/mtd/spi-nor/fsl-quadspi.c -+++ b/drivers/mtd/spi-nor/fsl-quadspi.c -@@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor - { - 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); - -@@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor - 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, SPINOR_OP_WREN, NULL, 0, 0); -- if (ret) -- return ret; -- - ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); - if (ret) - return ret; ---- a/drivers/mtd/spi-nor/spi-nor.c -+++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -163,81 +163,69 @@ static inline int set_4byte(struct spi_n - return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); - } - } -- --static int spi_nor_wait_till_ready(struct spi_nor *nor) -+static inline int spi_nor_sr_ready(struct spi_nor *nor) - { -- unsigned long deadline; -- int sr; -- -- deadline = jiffies + MAX_READY_WAIT_JIFFIES; -- -- do { -- cond_resched(); -+ int sr = read_sr(nor); -+ if (sr < 0) -+ return sr; -+ else -+ return !(sr & SR_WIP); -+} - -- sr = read_sr(nor); -- if (sr < 0) -- break; -- else if (!(sr & SR_WIP)) -- return 0; -- } while (!time_after_eq(jiffies, deadline)); -+static inline int spi_nor_fsr_ready(struct spi_nor *nor) -+{ -+ int fsr = read_fsr(nor); -+ if (fsr < 0) -+ return fsr; -+ else -+ return fsr & FSR_READY; -+} - -- return -ETIMEDOUT; -+static int spi_nor_ready(struct spi_nor *nor) -+{ -+ int sr, fsr; -+ sr = spi_nor_sr_ready(nor); -+ if (sr < 0) -+ return sr; -+ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; -+ if (fsr < 0) -+ return sr; -+ return sr && fsr; - } - --static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) -+/* -+ * Service routine to read status register until ready, or timeout occurs. -+ * Returns non-zero if error. -+ */ -+static int spi_nor_wait_till_ready(struct spi_nor *nor) - { - unsigned long deadline; -- int sr; -- int fsr; -+ int ret; - - deadline = jiffies + MAX_READY_WAIT_JIFFIES; - - do { - cond_resched(); - -- sr = read_sr(nor); -- if (sr < 0) { -- break; -- } else if (!(sr & SR_WIP)) { -- fsr = read_fsr(nor); -- if (fsr < 0) -- break; -- if (fsr & FSR_READY) -- return 0; -- } -+ ret = spi_nor_ready(nor); -+ if (ret < 0) -+ return ret; -+ if (ret) -+ 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, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); - } - -@@ -290,6 +278,8 @@ static int spi_nor_erase(struct mtd_info - if (ret) - return ret; - -+ write_enable(nor); -+ - /* whole-chip erase? */ - if (len == mtd->size) { - if (erase_chip(nor)) { -@@ -297,6 +287,10 @@ static int spi_nor_erase(struct mtd_info - goto erase_err; - } - -+ ret = spi_nor_wait_till_ready(nor); -+ if (ret) -+ goto erase_err; -+ - /* REVISIT in some cases we could speed up erasing large regions - * 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. -@@ -312,9 +306,15 @@ static int spi_nor_erase(struct mtd_info - - addr += mtd->erasesize; - len -= mtd->erasesize; -+ -+ ret = spi_nor_wait_till_ready(nor); -+ if (ret) -+ goto erase_err; - } - } - -+ write_disable(nor); -+ - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); - - instr->state = MTD_ERASE_DONE; -@@ -339,11 +339,6 @@ static int spi_nor_lock(struct mtd_info - 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)) -@@ -386,11 +381,6 @@ static int spi_nor_unlock(struct mtd_inf - 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)) -@@ -702,11 +692,6 @@ static int sst_write(struct mtd_info *mt - 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; -@@ -718,7 +703,7 @@ static int sst_write(struct mtd_info *mt - - /* write one byte. */ - nor->write(nor, to, 1, retlen, buf); -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - } -@@ -730,7 +715,7 @@ static int sst_write(struct mtd_info *mt - - /* write two bytes. */ - nor->write(nor, to, 2, retlen, buf + actual); -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - to += 2; -@@ -739,7 +724,7 @@ static int sst_write(struct mtd_info *mt - nor->sst_write_second = false; - - write_disable(nor); -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - -@@ -750,7 +735,7 @@ static int sst_write(struct mtd_info *mt - nor->program_opcode = SPINOR_OP_BP; - nor->write(nor, to, 1, retlen, buf + actual); - -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - write_disable(nor); -@@ -778,11 +763,6 @@ static int spi_nor_write(struct mtd_info - 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); -@@ -801,16 +781,20 @@ static int spi_nor_write(struct mtd_info - if (page_size > nor->page_size) - page_size = nor->page_size; - -- wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); -+ if (ret) -+ goto write_err; -+ - write_enable(nor); - - nor->write(nor, to + i, page_size, retlen, buf + i); - } - } - -+ ret = spi_nor_wait_till_ready(nor); - write_err: - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); -- return 0; -+ return ret; - } - - static int macronix_quad_enable(struct spi_nor *nor) -@@ -823,7 +807,7 @@ static int macronix_quad_enable(struct s - nor->cmd_buf[0] = val | SR_QUAD_EN_MX; - nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); - -- if (wait_till_ready(nor)) -+ if (spi_nor_wait_till_ready(nor)) - return 1; - - ret = read_sr(nor); -@@ -905,8 +889,6 @@ static int spi_nor_check(struct spi_nor - - 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; - } -@@ -977,9 +959,8 @@ int spi_nor_scan(struct spi_nor *nor, co - else - mtd->_write = spi_nor_write; - -- if ((info->flags & USE_FSR) && -- nor->wait_till_ready == spi_nor_wait_till_ready) -- nor->wait_till_ready = spi_nor_wait_till_fsr_ready; -+ if (info->flags & USE_FSR) -+ nor->flags |= SNOR_F_USE_FSR; - - #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS - /* prefer "small sector" erase if possible */ ---- a/include/linux/mtd/spi-nor.h -+++ b/include/linux/mtd/spi-nor.h -@@ -116,6 +116,10 @@ enum spi_nor_ops { - SPI_NOR_OPS_UNLOCK, - }; - -+enum spi_nor_option_flags { -+ SNOR_F_USE_FSR = BIT(0), -+}; -+ - /** - * struct spi_nor - Structure for defining a the SPI NOR layer - * @mtd: point to a mtd_info structure -@@ -129,6 +133,7 @@ enum spi_nor_ops { - * @program_opcode: the program opcode - * @flash_read: the mode of the read - * @sst_write_second: used by the SST write operation -+ * @flags: flag options for the current SPI-NOR (SNOR_F_*) - * @cfg: used by the read_xfer/write_xfer - * @cmd_buf: used by the write_reg - * @prepare: [OPTIONAL] do some preparations for the -@@ -141,7 +146,6 @@ enum spi_nor_ops { - * @write_reg: [DRIVER-SPECIFIC] write data to the register - * @read_id: [REPLACEABLE] read out the ID data, and find - * the proper spi_device_id -- * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready - * @read: [DRIVER-SPECIFIC] read data from the SPI NOR - * @write: [DRIVER-SPECIFIC] write data to the SPI NOR - * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR -@@ -160,6 +164,7 @@ struct spi_nor { - u8 program_opcode; - enum read_mode flash_read; - bool sst_write_second; -+ u32 flags; - struct spi_nor_xfer_cfg cfg; - u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; - -@@ -173,7 +178,6 @@ struct spi_nor { - int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, - int write_enable); - const struct spi_device_id *(*read_id)(struct spi_nor *nor); -- int (*wait_till_ready)(struct spi_nor *nor); - - int (*read)(struct spi_nor *nor, loff_t from, - size_t len, size_t *retlen, u_char *read_buf); diff --git a/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch b/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch index 9a9d903f82..41ef3b300e 100644 --- a/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch +++ b/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch @@ -17,247 +17,3 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o +obj-$(CONFIG_MTD_SPI_BCM53XXSPIFLASH) += bcm53xxspiflash.o ---- /dev/null -+++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c -@@ -0,0 +1,241 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static const char * const probes[] = { "bcm47xxpart", NULL }; -+ -+struct bcm53xxsf { -+ struct spi_device *spi; -+ struct mtd_info mtd; -+ struct spi_nor nor; -+}; -+ -+/************************************************** -+ * spi-nor API -+ **************************************************/ -+ -+static int bcm53xxspiflash_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -+ int len) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ -+ return spi_write_then_read(b53sf->spi, &opcode, 1, buf, len); -+} -+ -+static int bcm53xxspiflash_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -+ int len, int write_enable) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ u8 *cmd = kzalloc(len + 1, GFP_KERNEL); -+ int err; -+ -+ if (!cmd) -+ return -ENOMEM; -+ -+ cmd[0] = opcode; -+ memcpy(&cmd[1], buf, len); -+ err = spi_write(b53sf->spi, cmd, len + 1); -+ -+ kfree(cmd); -+ -+ return err; -+} -+ -+static int bcm53xxspiflash_read(struct spi_nor *nor, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ struct spi_message m; -+ struct spi_transfer t[2] = { { 0 }, { 0 } }; -+ unsigned char cmd[5]; -+ int cmd_len = 0; -+ int err; -+ -+ spi_message_init(&m); -+ -+ cmd[cmd_len++] = SPINOR_OP_READ; -+ if (b53sf->mtd.size > 0x1000000) -+ cmd[cmd_len++] = (from & 0xFF000000) >> 24; -+ cmd[cmd_len++] = (from & 0x00FF0000) >> 16; -+ cmd[cmd_len++] = (from & 0x0000FF00) >> 8; -+ cmd[cmd_len++] = (from & 0x000000FF) >> 0; -+ -+ t[0].tx_buf = cmd; -+ t[0].len = cmd_len; -+ spi_message_add_tail(&t[0], &m); -+ -+ t[1].rx_buf = buf; -+ t[1].len = len; -+ spi_message_add_tail(&t[1], &m); -+ -+ err = spi_sync(b53sf->spi, &m); -+ if (err) -+ return err; -+ -+ if (retlen && m.actual_length > cmd_len) -+ *retlen = m.actual_length - cmd_len; -+ -+ return 0; -+} -+ -+static void bcm53xxspiflash_write(struct spi_nor *nor, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ struct spi_message m; -+ struct spi_transfer t = { 0 }; -+ u8 *cmd = kzalloc(len + 5, GFP_KERNEL); -+ int cmd_len = 0; -+ int err; -+ -+ if (!cmd) -+ return; -+ -+ spi_message_init(&m); -+ -+ cmd[cmd_len++] = nor->program_opcode; -+ if (b53sf->mtd.size > 0x1000000) -+ cmd[cmd_len++] = (to & 0xFF000000) >> 24; -+ cmd[cmd_len++] = (to & 0x00FF0000) >> 16; -+ cmd[cmd_len++] = (to & 0x0000FF00) >> 8; -+ cmd[cmd_len++] = (to & 0x000000FF) >> 0; -+ memcpy(&cmd[cmd_len], buf, len); -+ -+ t.tx_buf = cmd; -+ t.len = cmd_len + len; -+ spi_message_add_tail(&t, &m); -+ -+ err = spi_sync(b53sf->spi, &m); -+ if (err) -+ goto out; -+ -+ if (retlen && m.actual_length > cmd_len) -+ *retlen += m.actual_length - cmd_len; -+ -+out: -+ kfree(cmd); -+} -+ -+static int bcm53xxspiflash_erase(struct spi_nor *nor, loff_t offs) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ unsigned char cmd[5]; -+ int i; -+ -+ i = 0; -+ cmd[i++] = nor->erase_opcode; -+ if (b53sf->mtd.size > 0x1000000) -+ cmd[i++] = (offs & 0xFF000000) >> 24; -+ cmd[i++] = ((offs & 0x00FF0000) >> 16); -+ cmd[i++] = ((offs & 0x0000FF00) >> 8); -+ cmd[i++] = ((offs & 0x000000FF) >> 0); -+ -+ return spi_write(b53sf->spi, cmd, i); -+} -+ -+static const struct spi_device_id *bcm53xxspiflash_read_id(struct spi_nor *nor) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ struct device *dev = &b53sf->spi->dev; -+ const struct spi_device_id *id; -+ unsigned char cmd[4]; -+ unsigned char resp[2]; -+ char *name = NULL; -+ int err; -+ -+ /* SST and Winbond/NexFlash specific command */ -+ cmd[0] = 0x90; /* Read Manufacturer / Device ID */ -+ cmd[1] = 0; -+ cmd[2] = 0; -+ cmd[3] = 0; -+ err = spi_write_then_read(b53sf->spi, cmd, 4, resp, 2); -+ if (err < 0) { -+ dev_err(dev, "error reading SPI flash id\n"); -+ return ERR_PTR(-EBUSY); -+ } -+ switch (resp[0]) { -+ case 0xef: /* Winbond/NexFlash */ -+ switch (resp[1]) { -+ case 0x17: -+ name = "w25q128"; -+ break; -+ } -+ if (!name) { -+ dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n", -+ resp[0], resp[1]); -+ return ERR_PTR(-ENOTSUPP); -+ } -+ goto found_name; -+ } -+ -+ /* TODO: Try more ID commands */ -+ -+ return ERR_PTR(-ENODEV); -+ -+found_name: -+ id = spi_nor_match_id(name); -+ if (!id) { -+ dev_err(dev, "No matching entry for %s flash\n", name); -+ return ERR_PTR(-ENOENT); -+ } -+ -+ return id; -+} -+ -+/************************************************** -+ * SPI driver -+ **************************************************/ -+ -+static int bcm53xxspiflash_probe(struct spi_device *spi) -+{ -+ struct bcm53xxsf *b53sf; -+ int err; -+ -+ b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL); -+ if (!b53sf) -+ return -ENOMEM; -+ spi_set_drvdata(spi, b53sf); -+ -+ b53sf->spi = spi; -+ -+ b53sf->mtd.priv = &b53sf->nor; -+ -+ b53sf->nor.mtd = &b53sf->mtd; -+ b53sf->nor.dev = &spi->dev; -+ b53sf->nor.read_reg = bcm53xxspiflash_read_reg; -+ b53sf->nor.write_reg = bcm53xxspiflash_write_reg; -+ b53sf->nor.read = bcm53xxspiflash_read; -+ b53sf->nor.write = bcm53xxspiflash_write; -+ b53sf->nor.erase = bcm53xxspiflash_erase; -+ b53sf->nor.read_id = bcm53xxspiflash_read_id; -+ b53sf->nor.priv = b53sf; -+ -+ err = spi_nor_scan(&b53sf->nor, NULL, SPI_NOR_NORMAL); -+ if (err) -+ return err; -+ -+ err = mtd_device_parse_register(&b53sf->mtd, probes, NULL, NULL, 0); -+ if (err) -+ return err; -+ -+ return 0; -+} -+ -+static int bcm53xxspiflash_remove(struct spi_device *spi) -+{ -+ return 0; -+} -+ -+static struct spi_driver bcm53xxspiflash_driver = { -+ .driver = { -+ .name = "bcm53xxspiflash", -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm53xxspiflash_probe, -+ .remove = bcm53xxspiflash_remove, -+}; -+ -+module_spi_driver(bcm53xxspiflash_driver); diff --git a/target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch b/target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch deleted file mode 100644 index f2bb0542b9..0000000000 --- a/target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch +++ /dev/null @@ -1,42 +0,0 @@ ---- a/drivers/mtd/spi-nor/bcm53xxspiflash.c -+++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c -@@ -173,7 +173,8 @@ static const struct spi_device_id *bcm53 - - /* TODO: Try more ID commands */ - -- return ERR_PTR(-ENODEV); -+ /* Some chips used by Broadcom may actually support JEDEC */ -+ return spi_nor_read_id(nor); - - found_name: - id = spi_nor_match_id(name); ---- a/drivers/mtd/spi-nor/spi-nor.c -+++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -629,7 +629,7 @@ const struct spi_device_id spi_nor_ids[] - }; - EXPORT_SYMBOL_GPL(spi_nor_ids); - --static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) -+const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) - { - int tmp; - u8 id[5]; -@@ -660,6 +660,7 @@ static const struct spi_device_id *spi_n - dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); - return ERR_PTR(-ENODEV); - } -+EXPORT_SYMBOL_GPL(spi_nor_read_id); - - static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) ---- a/include/linux/mtd/spi-nor.h -+++ b/include/linux/mtd/spi-nor.h -@@ -188,6 +188,8 @@ struct spi_nor { - void *priv; - }; - -+const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor); -+ - /** - * spi_nor_scan() - scan the SPI NOR - * @nor: the spi_nor structure diff --git a/target/linux/bcm53xx/patches-3.18/003-mtd-spi-nor-from-3.19.patch b/target/linux/bcm53xx/patches-3.18/003-mtd-spi-nor-from-3.19.patch new file mode 100644 index 0000000000..2e5fa10a60 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.18/003-mtd-spi-nor-from-3.19.patch @@ -0,0 +1,662 @@ +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -26,7 +26,38 @@ + /* 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) ++#define SPI_NOR_MAX_ID_LEN 6 ++ ++struct flash_info { ++ /* ++ * This array stores the ID bytes. ++ * The first three bytes are the JEDIC ID. ++ * JEDEC ID zero means "no ID" (mostly older chips). ++ */ ++ u8 id[SPI_NOR_MAX_ID_LEN]; ++ u8 id_len; ++ ++ /* The size listed here is what works with SPINOR_OP_SE, which isn't ++ * necessarily called a "sector" by the vendor. ++ */ ++ unsigned sector_size; ++ u16 n_sectors; ++ ++ u16 page_size; ++ u16 addr_width; ++ ++ u16 flags; ++#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 /* 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 */ ++#define USE_FSR 0x80 /* use flag status register */ ++}; ++ ++#define JEDEC_MFR(info) ((info)->id[0]) + + static const struct spi_device_id *spi_nor_match_id(const char *name); + +@@ -98,7 +129,7 @@ static inline int spi_nor_read_dummy_cyc + case SPI_NOR_FAST: + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: +- return 1; ++ return 8; + case SPI_NOR_NORMAL: + return 0; + } +@@ -138,13 +169,14 @@ static inline struct spi_nor *mtd_to_spi + } + + /* Enable/disable 4-byte addressing mode. */ +-static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) ++static inline int set_4byte(struct spi_nor *nor, struct flash_info *info, ++ int enable) + { + int status; + bool need_wren = false; + u8 cmd; + +- switch (JEDEC_MFR(jedec_id)) { ++ switch (JEDEC_MFR(info)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; +@@ -165,81 +197,74 @@ static inline int set_4byte(struct spi_n + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); + } + } +- +-static int spi_nor_wait_till_ready(struct spi_nor *nor) ++static inline int spi_nor_sr_ready(struct spi_nor *nor) + { +- unsigned long deadline; +- int sr; +- +- deadline = jiffies + MAX_READY_WAIT_JIFFIES; +- +- do { +- cond_resched(); ++ int sr = read_sr(nor); ++ if (sr < 0) ++ return sr; ++ else ++ return !(sr & SR_WIP); ++} + +- sr = read_sr(nor); +- if (sr < 0) +- break; +- else if (!(sr & SR_WIP)) +- return 0; +- } while (!time_after_eq(jiffies, deadline)); ++static inline int spi_nor_fsr_ready(struct spi_nor *nor) ++{ ++ int fsr = read_fsr(nor); ++ if (fsr < 0) ++ return fsr; ++ else ++ return fsr & FSR_READY; ++} + +- return -ETIMEDOUT; ++static int spi_nor_ready(struct spi_nor *nor) ++{ ++ int sr, fsr; ++ sr = spi_nor_sr_ready(nor); ++ if (sr < 0) ++ return sr; ++ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; ++ if (fsr < 0) ++ return fsr; ++ return sr && fsr; + } + +-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) ++/* ++ * Service routine to read status register until ready, or timeout occurs. ++ * Returns non-zero if error. ++ */ ++static int spi_nor_wait_till_ready(struct spi_nor *nor) + { + unsigned long deadline; +- int sr; +- int fsr; ++ int timeout = 0, ret; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + +- do { ++ while (!timeout) { ++ if (time_after_eq(jiffies, deadline)) ++ timeout = 1; ++ ++ ret = spi_nor_ready(nor); ++ if (ret < 0) ++ return ret; ++ if (ret) ++ return 0; ++ + cond_resched(); ++ } + +- sr = read_sr(nor); +- if (sr < 0) { +- break; +- } else if (!(sr & SR_WIP)) { +- fsr = read_fsr(nor); +- if (fsr < 0) +- break; +- if (fsr & FSR_READY) +- return 0; +- } +- } while (!time_after_eq(jiffies, deadline)); ++ dev_err(nor->dev, "flash operation timed out\n"); + + 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, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); + } + +@@ -294,11 +319,17 @@ static int spi_nor_erase(struct mtd_info + + /* whole-chip erase? */ + if (len == mtd->size) { ++ write_enable(nor); ++ + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto erase_err; ++ + /* REVISIT in some cases we could speed up erasing large regions + * 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. +@@ -307,6 +338,8 @@ static int spi_nor_erase(struct mtd_info + /* "sector"-at-a-time erase */ + } else { + while (len) { ++ write_enable(nor); ++ + if (nor->erase(nor, addr)) { + ret = -EIO; + goto erase_err; +@@ -314,9 +347,15 @@ static int spi_nor_erase(struct mtd_info + + addr += mtd->erasesize; + len -= mtd->erasesize; ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto erase_err; + } + } + ++ write_disable(nor); ++ + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + + instr->state = MTD_ERASE_DONE; +@@ -341,11 +380,6 @@ static int spi_nor_lock(struct mtd_info + 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)) +@@ -388,11 +422,6 @@ static int spi_nor_unlock(struct mtd_inf + 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)) +@@ -424,38 +453,34 @@ err: + 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 SPINOR_OP_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 /* 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 /* 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 */ +-#define USE_FSR 0x80 /* use flag status register */ +-}; +- ++/* Used when the "_ext_id" is two bytes at most */ + #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), \ ++ .id = { \ ++ ((_jedec_id) >> 16) & 0xff, \ ++ ((_jedec_id) >> 8) & 0xff, \ ++ (_jedec_id) & 0xff, \ ++ ((_ext_id) >> 8) & 0xff, \ ++ (_ext_id) & 0xff, \ ++ }, \ ++ .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ ++ .sector_size = (_sector_size), \ ++ .n_sectors = (_n_sectors), \ ++ .page_size = 256, \ ++ .flags = (_flags), \ ++ }) ++ ++#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ ++ ((kernel_ulong_t)&(struct flash_info) { \ ++ .id = { \ ++ ((_jedec_id) >> 16) & 0xff, \ ++ ((_jedec_id) >> 8) & 0xff, \ ++ (_jedec_id) & 0xff, \ ++ ((_ext_id) >> 16) & 0xff, \ ++ ((_ext_id) >> 8) & 0xff, \ ++ (_ext_id) & 0xff, \ ++ }, \ ++ .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ +@@ -507,6 +532,9 @@ static const struct spi_device_id spi_no + { "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) }, + ++ /* Fujitsu */ ++ { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, ++ + /* GigaDevice */ + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, +@@ -532,6 +560,7 @@ static const struct spi_device_id spi_no + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + + /* Micron */ ++ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, +@@ -556,6 +585,7 @@ static const struct spi_device_id spi_no + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, ++ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, +@@ -577,6 +607,7 @@ static const struct spi_device_id spi_no + { "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) }, ++ { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, +@@ -588,7 +619,6 @@ static const struct spi_device_id spi_no + { "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) }, +@@ -644,32 +674,24 @@ static const struct spi_device_id spi_no + static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) + { + int tmp; +- u8 id[5]; +- u32 jedec; +- u16 ext_jedec; ++ u8 id[SPI_NOR_MAX_ID_LEN]; + struct flash_info *info; + +- tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); ++ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + 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) ++ if (info->id_len) { ++ if (!memcmp(info->id, id, info->id_len)) + return &spi_nor_ids[tmp]; + } + } +- dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); ++ dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n", ++ id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); + } + +@@ -704,11 +726,6 @@ static int sst_write(struct mtd_info *mt + 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; +@@ -720,7 +737,7 @@ static int sst_write(struct mtd_info *mt + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + } +@@ -732,7 +749,7 @@ static int sst_write(struct mtd_info *mt + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; +@@ -741,7 +758,7 @@ static int sst_write(struct mtd_info *mt + nor->sst_write_second = false; + + write_disable(nor); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + +@@ -752,7 +769,7 @@ static int sst_write(struct mtd_info *mt + nor->program_opcode = SPINOR_OP_BP; + nor->write(nor, to, 1, retlen, buf + actual); + +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); +@@ -780,11 +797,6 @@ static int spi_nor_write(struct mtd_info + 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); +@@ -803,16 +815,20 @@ static int spi_nor_write(struct mtd_info + if (page_size > nor->page_size) + page_size = nor->page_size; + +- wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto write_err; ++ + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + ++ ret = spi_nor_wait_till_ready(nor); + write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); +- return 0; ++ return ret; + } + + static int macronix_quad_enable(struct spi_nor *nor) +@@ -825,7 +841,7 @@ static int macronix_quad_enable(struct s + nor->cmd_buf[0] = val | SR_QUAD_EN_MX; + nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); + +- if (wait_till_ready(nor)) ++ if (spi_nor_wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); +@@ -875,11 +891,11 @@ static int spansion_quad_enable(struct s + return 0; + } + +-static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) ++static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) + { + int status; + +- switch (JEDEC_MFR(jedec_id)) { ++ switch (JEDEC_MFR(info)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { +@@ -905,11 +921,6 @@ static int spi_nor_check(struct spi_nor + 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; + } + +@@ -927,16 +938,24 @@ int spi_nor_scan(struct spi_nor *nor, co + if (ret) + return ret; + +- id = spi_nor_match_id(name); +- if (!id) ++ /* Try to auto-detect if chip name wasn't specified */ ++ if (!name) ++ id = spi_nor_read_id(nor); ++ else ++ id = spi_nor_match_id(name); ++ if (IS_ERR_OR_NULL(id)) + return -ENOENT; + + info = (void *)id->driver_data; + +- if (info->jedec_id) { ++ /* ++ * If caller has specified name of flash model that can normally be ++ * detected using JEDEC, let's verify it. ++ */ ++ if (name && info->id_len) { + const struct spi_device_id *jid; + +- jid = nor->read_id(nor); ++ jid = spi_nor_read_id(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { +@@ -961,9 +980,9 @@ int spi_nor_scan(struct spi_nor *nor, co + * 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) { ++ if (JEDEC_MFR(info) == CFI_MFR_ATMEL || ++ JEDEC_MFR(info) == CFI_MFR_INTEL || ++ JEDEC_MFR(info) == CFI_MFR_SST) { + write_enable(nor); + write_sr(nor, 0); + } +@@ -978,7 +997,7 @@ int spi_nor_scan(struct spi_nor *nor, co + mtd->_read = spi_nor_read; + + /* nor protection support for STmicro chips */ +- if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { ++ if (JEDEC_MFR(info) == CFI_MFR_ST) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + } +@@ -989,9 +1008,8 @@ int spi_nor_scan(struct spi_nor *nor, co + else + mtd->_write = spi_nor_write; + +- if ((info->flags & USE_FSR) && +- nor->wait_till_ready == spi_nor_wait_till_ready) +- nor->wait_till_ready = spi_nor_wait_till_fsr_ready; ++ if (info->flags & USE_FSR) ++ nor->flags |= SNOR_F_USE_FSR; + + #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ +@@ -1032,7 +1050,7 @@ int spi_nor_scan(struct spi_nor *nor, co + + /* 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); ++ ret = set_quad_mode(nor, info); + if (ret) { + dev_err(dev, "quad mode not supported\n"); + return ret; +@@ -1068,7 +1086,7 @@ int spi_nor_scan(struct spi_nor *nor, co + 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) { ++ if (JEDEC_MFR(info) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: +@@ -1089,7 +1107,7 @@ int spi_nor_scan(struct spi_nor *nor, co + nor->erase_opcode = SPINOR_OP_SE_4B; + mtd->erasesize = info->sector_size; + } else +- set_4byte(nor, info->jedec_id, 1); ++ set_4byte(nor, info, 1); + } else { + nor->addr_width = 3; + } +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -116,6 +116,10 @@ enum spi_nor_ops { + SPI_NOR_OPS_UNLOCK, + }; + ++enum spi_nor_option_flags { ++ SNOR_F_USE_FSR = BIT(0), ++}; ++ + /** + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure +@@ -129,6 +133,7 @@ enum spi_nor_ops { + * @program_opcode: the program opcode + * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation ++ * @flags: flag options for the current SPI-NOR (SNOR_F_*) + * @cfg: used by the read_xfer/write_xfer + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the +@@ -139,9 +144,6 @@ enum spi_nor_ops { + * @write_xfer: [OPTIONAL] the writefundamental primitive + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register +- * @read_id: [REPLACEABLE] read out the ID data, and find +- * the proper spi_device_id +- * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR +@@ -160,6 +162,7 @@ struct spi_nor { + u8 program_opcode; + enum read_mode flash_read; + bool sst_write_second; ++ u32 flags; + struct spi_nor_xfer_cfg cfg; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + +@@ -172,8 +175,6 @@ struct spi_nor { + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable); +- const struct spi_device_id *(*read_id)(struct spi_nor *nor); +- int (*wait_till_ready)(struct spi_nor *nor); + + int (*read)(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *read_buf); diff --git a/target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch b/target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch deleted file mode 100644 index b72e2e2613..0000000000 --- a/target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- a/drivers/mtd/spi-nor/spi-nor.c -+++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -931,29 +931,23 @@ int spi_nor_scan(struct spi_nor *nor, co - if (!id) - return -ENOENT; - -- info = (void *)id->driver_data; -- -- if (info->jedec_id) { -- const struct spi_device_id *jid; -- -- jid = nor->read_id(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; -+ if (id) { -+ info = (void *)id->driver_data; -+ if (info->jedec_id) { -+ dev_warn(dev, -+ "passed SPI device ID (%s) contains JEDEC, ignoring it, driver should be fixed!\n", -+ id->name); -+ id = NULL; - } - } - -+ if (!id) { -+ id = nor->read_id(nor); -+ if (IS_ERR(id)) -+ return PTR_ERR(id); -+ } -+ info = (void *)id->driver_data; -+ - mutex_init(&nor->lock); - - /* diff --git a/target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch b/target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch deleted file mode 100644 index 08e53a7418..0000000000 --- a/target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch +++ /dev/null @@ -1,374 +0,0 @@ ---- a/drivers/mtd/spi-nor/fsl-quadspi.c -+++ b/drivers/mtd/spi-nor/fsl-quadspi.c -@@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor - { - 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); - -@@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor - 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, SPINOR_OP_WREN, NULL, 0, 0); -- if (ret) -- return ret; -- - ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); - if (ret) - return ret; ---- a/drivers/mtd/spi-nor/spi-nor.c -+++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -165,81 +165,69 @@ static inline int set_4byte(struct spi_n - return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); - } - } -- --static int spi_nor_wait_till_ready(struct spi_nor *nor) -+static inline int spi_nor_sr_ready(struct spi_nor *nor) - { -- unsigned long deadline; -- int sr; -- -- deadline = jiffies + MAX_READY_WAIT_JIFFIES; -- -- do { -- cond_resched(); -+ int sr = read_sr(nor); -+ if (sr < 0) -+ return sr; -+ else -+ return !(sr & SR_WIP); -+} - -- sr = read_sr(nor); -- if (sr < 0) -- break; -- else if (!(sr & SR_WIP)) -- return 0; -- } while (!time_after_eq(jiffies, deadline)); -+static inline int spi_nor_fsr_ready(struct spi_nor *nor) -+{ -+ int fsr = read_fsr(nor); -+ if (fsr < 0) -+ return fsr; -+ else -+ return fsr & FSR_READY; -+} - -- return -ETIMEDOUT; -+static int spi_nor_ready(struct spi_nor *nor) -+{ -+ int sr, fsr; -+ sr = spi_nor_sr_ready(nor); -+ if (sr < 0) -+ return sr; -+ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; -+ if (fsr < 0) -+ return sr; -+ return sr && fsr; - } - --static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) -+/* -+ * Service routine to read status register until ready, or timeout occurs. -+ * Returns non-zero if error. -+ */ -+static int spi_nor_wait_till_ready(struct spi_nor *nor) - { - unsigned long deadline; -- int sr; -- int fsr; -+ int ret; - - deadline = jiffies + MAX_READY_WAIT_JIFFIES; - - do { - cond_resched(); - -- sr = read_sr(nor); -- if (sr < 0) { -- break; -- } else if (!(sr & SR_WIP)) { -- fsr = read_fsr(nor); -- if (fsr < 0) -- break; -- if (fsr & FSR_READY) -- return 0; -- } -+ ret = spi_nor_ready(nor); -+ if (ret < 0) -+ return ret; -+ if (ret) -+ 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, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); - } - -@@ -292,6 +280,8 @@ static int spi_nor_erase(struct mtd_info - if (ret) - return ret; - -+ write_enable(nor); -+ - /* whole-chip erase? */ - if (len == mtd->size) { - if (erase_chip(nor)) { -@@ -299,6 +289,10 @@ static int spi_nor_erase(struct mtd_info - goto erase_err; - } - -+ ret = spi_nor_wait_till_ready(nor); -+ if (ret) -+ goto erase_err; -+ - /* REVISIT in some cases we could speed up erasing large regions - * 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. -@@ -314,9 +308,15 @@ static int spi_nor_erase(struct mtd_info - - addr += mtd->erasesize; - len -= mtd->erasesize; -+ -+ ret = spi_nor_wait_till_ready(nor); -+ if (ret) -+ goto erase_err; - } - } - -+ write_disable(nor); -+ - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); - - instr->state = MTD_ERASE_DONE; -@@ -341,11 +341,6 @@ static int spi_nor_lock(struct mtd_info - 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)) -@@ -388,11 +383,6 @@ static int spi_nor_unlock(struct mtd_inf - 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)) -@@ -704,11 +694,6 @@ static int sst_write(struct mtd_info *mt - 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; -@@ -720,7 +705,7 @@ static int sst_write(struct mtd_info *mt - - /* write one byte. */ - nor->write(nor, to, 1, retlen, buf); -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - } -@@ -732,7 +717,7 @@ static int sst_write(struct mtd_info *mt - - /* write two bytes. */ - nor->write(nor, to, 2, retlen, buf + actual); -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - to += 2; -@@ -741,7 +726,7 @@ static int sst_write(struct mtd_info *mt - nor->sst_write_second = false; - - write_disable(nor); -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - -@@ -752,7 +737,7 @@ static int sst_write(struct mtd_info *mt - nor->program_opcode = SPINOR_OP_BP; - nor->write(nor, to, 1, retlen, buf + actual); - -- ret = wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); - if (ret) - goto time_out; - write_disable(nor); -@@ -780,11 +765,6 @@ static int spi_nor_write(struct mtd_info - 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); -@@ -803,16 +783,20 @@ static int spi_nor_write(struct mtd_info - if (page_size > nor->page_size) - page_size = nor->page_size; - -- wait_till_ready(nor); -+ ret = spi_nor_wait_till_ready(nor); -+ if (ret) -+ goto write_err; -+ - write_enable(nor); - - nor->write(nor, to + i, page_size, retlen, buf + i); - } - } - -+ ret = spi_nor_wait_till_ready(nor); - write_err: - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); -- return 0; -+ return ret; - } - - static int macronix_quad_enable(struct spi_nor *nor) -@@ -825,7 +809,7 @@ static int macronix_quad_enable(struct s - nor->cmd_buf[0] = val | SR_QUAD_EN_MX; - nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); - -- if (wait_till_ready(nor)) -+ if (spi_nor_wait_till_ready(nor)) - return 1; - - ret = read_sr(nor); -@@ -907,8 +891,6 @@ static int spi_nor_check(struct spi_nor - - 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; - } -@@ -983,9 +965,8 @@ int spi_nor_scan(struct spi_nor *nor, co - else - mtd->_write = spi_nor_write; - -- if ((info->flags & USE_FSR) && -- nor->wait_till_ready == spi_nor_wait_till_ready) -- nor->wait_till_ready = spi_nor_wait_till_fsr_ready; -+ if (info->flags & USE_FSR) -+ nor->flags |= SNOR_F_USE_FSR; - - #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS - /* prefer "small sector" erase if possible */ ---- a/include/linux/mtd/spi-nor.h -+++ b/include/linux/mtd/spi-nor.h -@@ -116,6 +116,10 @@ enum spi_nor_ops { - SPI_NOR_OPS_UNLOCK, - }; - -+enum spi_nor_option_flags { -+ SNOR_F_USE_FSR = BIT(0), -+}; -+ - /** - * struct spi_nor - Structure for defining a the SPI NOR layer - * @mtd: point to a mtd_info structure -@@ -129,6 +133,7 @@ enum spi_nor_ops { - * @program_opcode: the program opcode - * @flash_read: the mode of the read - * @sst_write_second: used by the SST write operation -+ * @flags: flag options for the current SPI-NOR (SNOR_F_*) - * @cfg: used by the read_xfer/write_xfer - * @cmd_buf: used by the write_reg - * @prepare: [OPTIONAL] do some preparations for the -@@ -141,7 +146,6 @@ enum spi_nor_ops { - * @write_reg: [DRIVER-SPECIFIC] write data to the register - * @read_id: [REPLACEABLE] read out the ID data, and find - * the proper spi_device_id -- * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready - * @read: [DRIVER-SPECIFIC] read data from the SPI NOR - * @write: [DRIVER-SPECIFIC] write data to the SPI NOR - * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR -@@ -160,6 +164,7 @@ struct spi_nor { - u8 program_opcode; - enum read_mode flash_read; - bool sst_write_second; -+ u32 flags; - struct spi_nor_xfer_cfg cfg; - u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; - -@@ -173,7 +178,6 @@ struct spi_nor { - int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, - int write_enable); - const struct spi_device_id *(*read_id)(struct spi_nor *nor); -- int (*wait_till_ready)(struct spi_nor *nor); - - int (*read)(struct spi_nor *nor, loff_t from, - size_t len, size_t *retlen, u_char *read_buf); diff --git a/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch b/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch index 80e1d324d7..41ef3b300e 100644 --- a/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch +++ b/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch @@ -17,247 +17,3 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o +obj-$(CONFIG_MTD_SPI_BCM53XXSPIFLASH) += bcm53xxspiflash.o ---- /dev/null -+++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c -@@ -0,0 +1,241 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static const char * const probes[] = { "bcm47xxpart", NULL }; -+ -+struct bcm53xxsf { -+ struct spi_device *spi; -+ struct mtd_info mtd; -+ struct spi_nor nor; -+}; -+ -+/************************************************** -+ * spi-nor API -+ **************************************************/ -+ -+static int bcm53xxspiflash_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -+ int len) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ -+ return spi_write_then_read(b53sf->spi, &opcode, 1, buf, len); -+} -+ -+static int bcm53xxspiflash_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -+ int len, int write_enable) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ u8 *cmd = kzalloc(len + 1, GFP_KERNEL); -+ int err; -+ -+ if (!cmd) -+ return -ENOMEM; -+ -+ cmd[0] = opcode; -+ memcpy(&cmd[1], buf, len); -+ err = spi_write(b53sf->spi, cmd, len + 1); -+ -+ kfree(cmd); -+ -+ return err; -+} -+ -+static int bcm53xxspiflash_read(struct spi_nor *nor, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ struct spi_message m; -+ struct spi_transfer t[2] = { { 0 }, { 0 } }; -+ unsigned char cmd[5]; -+ int cmd_len = 0; -+ int err; -+ -+ spi_message_init(&m); -+ -+ cmd[cmd_len++] = SPINOR_OP_READ; -+ if (b53sf->mtd.size > 0x1000000) -+ cmd[cmd_len++] = (from & 0xFF000000) >> 24; -+ cmd[cmd_len++] = (from & 0x00FF0000) >> 16; -+ cmd[cmd_len++] = (from & 0x0000FF00) >> 8; -+ cmd[cmd_len++] = (from & 0x000000FF) >> 0; -+ -+ t[0].tx_buf = cmd; -+ t[0].len = cmd_len; -+ spi_message_add_tail(&t[0], &m); -+ -+ t[1].rx_buf = buf; -+ t[1].len = len; -+ spi_message_add_tail(&t[1], &m); -+ -+ err = spi_sync(b53sf->spi, &m); -+ if (err) -+ return err; -+ -+ if (retlen && m.actual_length > cmd_len) -+ *retlen = m.actual_length - cmd_len; -+ -+ return 0; -+} -+ -+static void bcm53xxspiflash_write(struct spi_nor *nor, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ struct spi_message m; -+ struct spi_transfer t = { 0 }; -+ u8 *cmd = kzalloc(len + 5, GFP_KERNEL); -+ int cmd_len = 0; -+ int err; -+ -+ if (!cmd) -+ return; -+ -+ spi_message_init(&m); -+ -+ cmd[cmd_len++] = nor->program_opcode; -+ if (b53sf->mtd.size > 0x1000000) -+ cmd[cmd_len++] = (to & 0xFF000000) >> 24; -+ cmd[cmd_len++] = (to & 0x00FF0000) >> 16; -+ cmd[cmd_len++] = (to & 0x0000FF00) >> 8; -+ cmd[cmd_len++] = (to & 0x000000FF) >> 0; -+ memcpy(&cmd[cmd_len], buf, len); -+ -+ t.tx_buf = cmd; -+ t.len = cmd_len + len; -+ spi_message_add_tail(&t, &m); -+ -+ err = spi_sync(b53sf->spi, &m); -+ if (err) -+ goto out; -+ -+ if (retlen && m.actual_length > cmd_len) -+ *retlen += m.actual_length - cmd_len; -+ -+out: -+ kfree(cmd); -+} -+ -+static int bcm53xxspiflash_erase(struct spi_nor *nor, loff_t offs) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ unsigned char cmd[5]; -+ int i; -+ -+ i = 0; -+ cmd[i++] = nor->erase_opcode; -+ if (b53sf->mtd.size > 0x1000000) -+ cmd[i++] = (offs & 0xFF000000) >> 24; -+ cmd[i++] = ((offs & 0x00FF0000) >> 16); -+ cmd[i++] = ((offs & 0x0000FF00) >> 8); -+ cmd[i++] = ((offs & 0x000000FF) >> 0); -+ -+ return spi_write(b53sf->spi, cmd, i); -+} -+ -+static const struct spi_device_id *bcm53xxspiflash_read_id(struct spi_nor *nor) -+{ -+ struct bcm53xxsf *b53sf = nor->priv; -+ struct device *dev = &b53sf->spi->dev; -+ const struct spi_device_id *id; -+ unsigned char cmd[4]; -+ unsigned char resp[2]; -+ char *name = NULL; -+ int err; -+ -+ /* SST and Winbond/NexFlash specific command */ -+ cmd[0] = 0x90; /* Read Manufacturer / Device ID */ -+ cmd[1] = 0; -+ cmd[2] = 0; -+ cmd[3] = 0; -+ err = spi_write_then_read(b53sf->spi, cmd, 4, resp, 2); -+ if (err < 0) { -+ dev_err(dev, "error reading SPI flash id\n"); -+ return ERR_PTR(-EBUSY); -+ } -+ switch (resp[0]) { -+ case 0xef: /* Winbond/NexFlash */ -+ switch (resp[1]) { -+ case 0x17: -+ name = "w25q128"; -+ break; -+ } -+ if (!name) { -+ dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n", -+ resp[0], resp[1]); -+ return ERR_PTR(-ENOTSUPP); -+ } -+ goto found_name; -+ } -+ -+ /* TODO: Try more ID commands */ -+ -+ return ERR_PTR(-ENODEV); -+ -+found_name: -+// id = spi_nor_match_id(name); -+// if (!id) { -+// dev_err(dev, "No matching entry for %s flash\n", name); -+// return ERR_PTR(-ENOENT); -+// } -+ -+ return id; -+} -+ -+/************************************************** -+ * SPI driver -+ **************************************************/ -+ -+static int bcm53xxspiflash_probe(struct spi_device *spi) -+{ -+ struct bcm53xxsf *b53sf; -+ int err; -+ -+ b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL); -+ if (!b53sf) -+ return -ENOMEM; -+ spi_set_drvdata(spi, b53sf); -+ -+ b53sf->spi = spi; -+ -+ b53sf->mtd.priv = &b53sf->nor; -+ -+ b53sf->nor.mtd = &b53sf->mtd; -+ b53sf->nor.dev = &spi->dev; -+ b53sf->nor.read_reg = bcm53xxspiflash_read_reg; -+ b53sf->nor.write_reg = bcm53xxspiflash_write_reg; -+ b53sf->nor.read = bcm53xxspiflash_read; -+ b53sf->nor.write = bcm53xxspiflash_write; -+ b53sf->nor.erase = bcm53xxspiflash_erase; -+ b53sf->nor.read_id = bcm53xxspiflash_read_id; -+ b53sf->nor.priv = b53sf; -+ -+ err = spi_nor_scan(&b53sf->nor, "w25q128", SPI_NOR_NORMAL); -+ if (err) -+ return err; -+ -+ err = mtd_device_parse_register(&b53sf->mtd, probes, NULL, NULL, 0); -+ if (err) -+ return err; -+ -+ return 0; -+} -+ -+static int bcm53xxspiflash_remove(struct spi_device *spi) -+{ -+ return 0; -+} -+ -+static struct spi_driver bcm53xxspiflash_driver = { -+ .driver = { -+ .name = "bcm53xxspiflash", -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm53xxspiflash_probe, -+ .remove = bcm53xxspiflash_remove, -+}; -+ -+module_spi_driver(bcm53xxspiflash_driver); diff --git a/target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch b/target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch deleted file mode 100644 index d97441f586..0000000000 --- a/target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch +++ /dev/null @@ -1,42 +0,0 @@ ---- a/drivers/mtd/spi-nor/bcm53xxspiflash.c -+++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c -@@ -173,7 +173,8 @@ static const struct spi_device_id *bcm53 - - /* TODO: Try more ID commands */ - -- return ERR_PTR(-ENODEV); -+ /* Some chips used by Broadcom may actually support JEDEC */ -+ return spi_nor_read_id(nor); - - found_name: - // id = spi_nor_match_id(name); ---- a/drivers/mtd/spi-nor/spi-nor.c -+++ b/drivers/mtd/spi-nor/spi-nor.c -@@ -631,7 +631,7 @@ static const struct spi_device_id spi_no - { }, - }; - --static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) -+const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) - { - int tmp; - u8 id[5]; -@@ -662,6 +662,7 @@ static const struct spi_device_id *spi_n - dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); - return ERR_PTR(-ENODEV); - } -+EXPORT_SYMBOL_GPL(spi_nor_read_id); - - static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) ---- a/include/linux/mtd/spi-nor.h -+++ b/include/linux/mtd/spi-nor.h -@@ -188,6 +188,8 @@ struct spi_nor { - void *priv; - }; - -+const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor); -+ - /** - * spi_nor_scan() - scan the SPI NOR - * @nor: the spi_nor structure -- 2.11.0