diff --git a/target/linux/bcm53xx/config-3.14 b/target/linux/bcm53xx/config-3.14 index f7035220ad..e0fd0fd2d2 100644 --- a/target/linux/bcm53xx/config-3.14 +++ b/target/linux/bcm53xx/config-3.14 @@ -168,6 +168,7 @@ CONFIG_MODULES_USE_ELF_REL=y CONFIG_MTD_BCM47XX_PARTS=y # CONFIG_MTD_PHYSMAP_OF is not set CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_MULTI_IRQ_HANDLER=y CONFIG_MUTEX_SPIN_ON_OWNER=y CONFIG_NEED_DMA_MAP_STATE=y diff --git a/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-queued-for-3.18.patch b/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-queued-for-3.18.patch new file mode 100644 index 0000000000..7505a17a7d --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/002-mtd-spi-nor-queued-for-3.18.patch @@ -0,0 +1,39 @@ +--- 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[] + { "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) }, ++ { "m25px80", INFO(0x207114, 0, 64 * 1024, 16, 0) }, + + /* 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[] + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, +- { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, +@@ -671,11 +671,6 @@ static const struct spi_device_id *spi_n + return ERR_PTR(-ENODEV); + } + +-static const struct spi_device_id *jedec_probe(struct spi_nor *nor) +-{ +- return nor->read_id(nor); +-} +- + static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) + { +@@ -958,7 +953,7 @@ int spi_nor_scan(struct spi_nor *nor, co + if (info->jedec_id) { + const struct spi_device_id *jid; + +- jid = jedec_probe(nor); ++ jid = nor->read_id(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { diff --git a/target/linux/bcm53xx/patches-3.14/400-mtd-move-support-for-struct-flash_platform_data-into.patch b/target/linux/bcm53xx/patches-3.14/400-mtd-move-support-for-struct-flash_platform_data-into.patch new file mode 100644 index 0000000000..8076f550ba --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/400-mtd-move-support-for-struct-flash_platform_data-into.patch @@ -0,0 +1,52 @@ +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -915,7 +915,6 @@ int spi_nor_scan(struct spi_nor *nor, co + enum read_mode mode) + { + struct flash_info *info; +- struct flash_platform_data *data; + struct device *dev = nor->dev; + struct mtd_info *mtd = nor->mtd; + struct device_node *np = dev->of_node; +@@ -926,28 +925,6 @@ int spi_nor_scan(struct spi_nor *nor, co + if (ret) + return ret; + +- /* Platform data helps sort out which chip type we have, as +- * well as how this board partitions it. If we don't have +- * a chip ID, try the JEDEC id commands; they'll work for most +- * newer chips, even if we don't recognize the particular chip. +- */ +- data = dev_get_platdata(dev); +- if (data && data->type) { +- const struct spi_device_id *plat_id; +- +- for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { +- plat_id = &spi_nor_ids[i]; +- if (strcmp(data->type, plat_id->name)) +- continue; +- break; +- } +- +- if (i < ARRAY_SIZE(spi_nor_ids) - 1) +- id = plat_id; +- else +- dev_warn(dev, "unrecognized id %s\n", data->type); +- } +- + info = (void *)id->driver_data; + + if (info->jedec_id) { +@@ -985,11 +962,8 @@ int spi_nor_scan(struct spi_nor *nor, co + write_sr(nor, 0); + } + +- if (data && data->name) +- mtd->name = data->name; +- else ++ if (!mtd->name) + mtd->name = dev_name(dev); +- + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; diff --git a/target/linux/bcm53xx/patches-3.14/401-mtd-spi-nor-add-Kconfig-option-to-disable-4K-sectors.patch b/target/linux/bcm53xx/patches-3.14/401-mtd-spi-nor-add-Kconfig-option-to-disable-4K-sectors.patch new file mode 100644 index 0000000000..395b0af1d2 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/401-mtd-spi-nor-add-Kconfig-option-to-disable-4K-sectors.patch @@ -0,0 +1,44 @@ +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -7,6 +7,20 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++config MTD_SPI_NOR_USE_4K_SECTORS ++ bool "Use small 4096 B erase sectors" ++ default y ++ help ++ Many flash memories support erasing small (4096 B) sectors. Depending ++ on the usage this feature may provide performance gain in comparison ++ to erasing whole blocks (32/64 KiB). ++ Changing small part of flash content is usually faster with small ++ sectors. On the other hand erasing should be faster when using 64 KiB ++ block instead of 16 × 4 KiB sectors. ++ ++ Please note that some tools/drivers/filesystems may not work with ++ 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). ++ + config SPI_FSL_QUADSPI + tristate "Freescale Quad SPI controller" + depends on ARCH_MXC +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -987,6 +987,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; + ++#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = SPINOR_OP_BE_4K; +@@ -994,7 +995,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; +- } else { ++ } else ++#endif ++ { + nor->erase_opcode = SPINOR_OP_SE; + mtd->erasesize = info->sector_size; + } 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 new file mode 100644 index 0000000000..6cc77528e0 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch @@ -0,0 +1,46 @@ +--- 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 new file mode 100644 index 0000000000..79a5131314 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/403-mtd-spi-nor-refactor-wait-till-ready.patch @@ -0,0 +1,374 @@ +--- 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);