--- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ struct m25p { struct spi_device *spi; struct spi_nor spi_nor; struct mtd_info mtd; + u16 chunk_size; u8 command[MAX_CMD_SIZE]; }; @@ -117,25 +119,14 @@ static inline unsigned int m25p80_rx_nbi } } -/* - * Read an address range from the nor chip. The address range - * may be any size provided it is within the physical boundaries. - */ -static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int __m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; struct spi_transfer t[2]; struct spi_message m; int dummy = nor->read_dummy; - int ret; - - /* Wait till previous write/erase is done. */ - ret = nor->wait_till_ready(nor); - if (ret) - return ret; - spi_message_init(&m); memset(t, 0, (sizeof t)); @@ -156,6 +147,84 @@ static int m25p80_read(struct spi_nor *n *retlen = m.actual_length - m25p_cmdsz(nor) - dummy; return 0; } +/* + * Read an address range from the nor chip. The address range + * may be any size provided it is within the physical boundaries. + */ +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + + /* Wait till previous write/erase is done. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + return __m25p80_read(nor, from, len, retlen, buf); +} + + +static void m25p80_chunked_write(struct spi_nor *nor, loff_t _from, size_t _len, + size_t *_retlen, const u_char *_buf) +{ + struct m25p *flash = nor->priv; + int chunk_size; + int retlen = 0; + + chunk_size = flash->chunk_size; + if (!chunk_size) + chunk_size = _len; + + if (nor->addr_width > 3) + chunk_size -= nor->addr_width - 3; + + while (retlen < _len) { + size_t len = min_t(int, chunk_size, _len - retlen); + const u_char *buf = _buf + retlen; + loff_t from = _from + retlen; + + nor->wait_till_ready(nor); + nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); + + m25p80_write(nor, from, len, &retlen, buf); + } + *_retlen += retlen; +} + +static int m25p80_chunked_read(struct spi_nor *nor, loff_t _from, size_t _len, + size_t *_retlen, u_char *_buf) +{ + struct m25p *flash = nor->priv; + int chunk_size; + int ret; + + /* Wait till previous write/erase is done. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + chunk_size = flash->chunk_size; + if (!chunk_size) + chunk_size = _len; + + *_retlen = 0; + + while (*_retlen < _len) { + size_t len = min_t(int, chunk_size, _len - *_retlen); + u_char *buf = _buf + *_retlen; + loff_t from = _from + *_retlen; + int retlen = 0; + int ret = __m25p80_read(nor, from, len, &retlen, buf); + + if (ret) + return ret; + + *_retlen += retlen; + } + + return 0; +} static int m25p80_erase(struct spi_nor *nor, loff_t offset) { @@ -197,6 +266,7 @@ static int m25p_probe(struct spi_device struct spi_nor *nor; enum read_mode mode = SPI_NOR_NORMAL; char *flash_name = NULL; + u32 val; int ret; data = dev_get_platdata(&spi->dev); @@ -244,6 +314,14 @@ static int m25p_probe(struct spi_device if (ret) return ret; + if (spi->dev.of_node && + !of_property_read_u32(spi->dev.of_node, "m25p,chunked-io", &val)) { + dev_warn(&spi->dev, "using chunked io\n"); + nor->read = m25p80_chunked_read; + nor->write = m25p80_chunked_write; + flash->chunk_size = val; + } + ppdata.of_node = spi->dev.of_node; return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,