Message ID | 1380191565-28640-1-git-send-email-sourav.poddar@ti.com |
---|---|
State | New, archived |
Headers | show |
On Thu, 2013-09-26 at 16:02 +0530, Sourav Poddar wrote: > Some flash also support quad read mode. > Adding support for adding quad mode in m25p80. > > Signed-off-by: Sourav Poddar <sourav.poddar@ti.com> This seems sane enough to me if Mark agrees that it's correct from the SPI point of view. Do we have a solution for the vf610-twr host controller, and is this going to work for that too? Or are we still stuck on questions like "how are we going to program the LUT to do what the request asks?" and "how *does* the request actually hand the required information to the controller?" I've seen a bunch of "*this* is how you indicate dummy cycles", and "*this* is how you request a quad-read transfer" type responses; have we got all the way there yet?
Hi David, On Thursday 26 September 2013 04:30 PM, David Woodhouse wrote: > On Thu, 2013-09-26 at 16:02 +0530, Sourav Poddar wrote: >> Some flash also support quad read mode. >> Adding support for adding quad mode in m25p80. >> >> Signed-off-by: Sourav Poddar<sourav.poddar@ti.com> > This seems sane enough to me if Mark agrees that it's correct from the > SPI point of view. > > Do we have a solution for the vf610-twr host controller, and is this > going to work for that too? Or are we still stuck on questions like "how > are we going to program the LUT to do what the request asks?" and "how > *does* the request actually hand the required information to the > controller?" > If the pupose of LUT is to just set the dummy cycles, and vf610-twr hardware state machine does not have have any other dependency on LUT, this patch should work. Yes, this patch also solves the issue of communicating the appropriate request to the handler through some spi transfer parameters already available in spi.c. As explained in one comment in my previous version, If you see spi.c, "spi-rx-bus-width" property is already added. We will set this property in dts to 4 in case of quad read. Once this is set, spi->mode will be set to SPI_RX_QUAD in spi.c. which can be used by mtd layer to decide whether its a quad read or not. Then, we can use tx_nbits/rx_nbits property of spi_transfer to communicate to the controller whether the ongoing transfer is a quad/single read/write. > I've seen a bunch of "*this* is how you indicate dummy cycles", and > "*this* is how you request a quad-read transfer" type responses; have we > got all the way there yet? >
On Thu, 2013-09-26 at 16:51 +0530, Sourav Poddar wrote: > Yes, this patch also solves the issue of communicating the appropriate > request to the handler through some spi transfer parameters already > available in spi.c. Great, thanks for confirming that. So making vf610-twr conform to the Linux SPI API and do the right thing according to the requests it receives is now Someone Else's Problem™ and I look forward to never having to think about it again. Have fun :)
On Thu, Sep 26, 2013 at 12:00:43PM +0100, David Woodhouse wrote: > This seems sane enough to me if Mark agrees that it's correct from the > SPI point of view. I didn't really look at the patch but since it doesn't involve changes to the SPI API providing it does the right thing it should be fine. > I've seen a bunch of "*this* is how you indicate dummy cycles", and > "*this* is how you request a quad-read transfer" type responses; have we > got all the way there yet? Nobody added an API for dummy cycles yet but it's fairly obvious how to add them if someone wants to do that. The quad read stuff is in mainline already.
于 2013年09月26日 19:21, Sourav Poddar 写道: > If the pupose of LUT is to just set the dummy cycles, and vf610-twr > hardware state machine does not have have any other dependency > on LUT, this patch should work. Hi Sourav & David: The key issue about the vf610-twr is that: [0] Use the LUT makes the Quadspi driver more efficiency. [1] the vf610-twr needs to know the SPI NOR commands for Page Program. Why? because the driver can not change the size of write-buffer from 256bytes to the 64byte(TXFIFO SIZE). [2] the dummy and other things. Mark said the Quadspi is not a SPI controller, instead it is a SPI NOR controller, and Mark suggested me to rewrite the m25p80.c for the Quadspi. thanks Huang Shijie
On Friday 27 September 2013 08:10 AM, Huang Shijie wrote: > 于 2013年09月26日 19:21, Sourav Poddar 写道: >> If the pupose of LUT is to just set the dummy cycles, and vf610-twr >> hardware state machine does not have have any other dependency >> on LUT, this patch should work. > Hi Sourav & David: > > The key issue about the vf610-twr is that: > [0] Use the LUT makes the Quadspi driver more efficiency. > [1] the vf610-twr needs to know the SPI NOR commands for Page Program. > Why? because the driver can not change the size of write-buffer > from 256bytes to the 64byte(TXFIFO SIZE). Not clear about this. But, you will anyway know what you are using from the m25p80 side rite? > [2] the dummy and other things. > Dummy stuffs can be handled from m25p80 side rite? fast read dummy cycle support is already there. While, $subject patch adds it for quad read. > Mark said the Quadspi is not a SPI controller, instead it is a SPI NOR > controller, and Mark suggested me > to rewrite the m25p80.c for the Quadspi. > > thanks > Huang Shijie > >
于 2013年09月27日 13:54, Sourav Poddar 写道: > On Friday 27 September 2013 08:10 AM, Huang Shijie wrote: >> 于 2013年09月26日 19:21, Sourav Poddar 写道: >>> If the pupose of LUT is to just set the dummy cycles, and vf610-twr >>> hardware state machine does not have have any other dependency >>> on LUT, this patch should work. >> Hi Sourav & David: >> >> The key issue about the vf610-twr is that: >> [0] Use the LUT makes the Quadspi driver more efficiency. >> [1] the vf610-twr needs to know the SPI NOR commands for Page >> Program. >> Why? because the driver can not change the size of >> write-buffer from 256bytes to the 64byte(TXFIFO SIZE). > Not clear about this. But, you will anyway know what you are using > from the m25p80 side rite? The TX FIFO is 64 bytes in the Vybrid, but the Page Program may writes 265 bytes per time. If the TX FIFO is smaller then the size of Page Program, we have to wait until the Write(64bytes) is finished. If we do not wait, the write will not finished. >> [2] the dummy and other things. >> > Dummy stuffs can be handled from m25p80 side rite? fast read dummy > cycle support is already there. While, > $subject patch adds it for quad read. The m25p80 can only handle the 8bit dummy now, such as fast read and QOR(0x6b). But it can not handle the QIOR (0XEB, may needs 4bit dummy), and can not handle the DDR QIOR(0XED, may needs 6bit dummy). thanks Huang Shijie
On Friday 27 September 2013 11:46 AM, Huang Shijie wrote: > 于 2013年09月27日 13:54, Sourav Poddar 写道: >> On Friday 27 September 2013 08:10 AM, Huang Shijie wrote: >>> 于 2013年09月26日 19:21, Sourav Poddar 写道: >>>> If the pupose of LUT is to just set the dummy cycles, and vf610-twr >>>> hardware state machine does not have have any other dependency >>>> on LUT, this patch should work. >>> Hi Sourav & David: >>> >>> The key issue about the vf610-twr is that: >>> [0] Use the LUT makes the Quadspi driver more efficiency. >>> [1] the vf610-twr needs to know the SPI NOR commands for Page >>> Program. >>> Why? because the driver can not change the size of >>> write-buffer from 256bytes to the 64byte(TXFIFO SIZE). >> Not clear about this. But, you will anyway know what you are using >> from the m25p80 side rite? > > The TX FIFO is 64 bytes in the Vybrid, but the Page Program may > writes 265 bytes per time. > > If the TX FIFO is smaller then the size of Page Program, we have to > wait until the Write(64bytes) is finished. > If we do not wait, the write will not finished. > > hmm..I think thats should be handle in your controller by checking t->tx_buf and see what command is used. > >>> [2] the dummy and other things. >>> >> Dummy stuffs can be handled from m25p80 side rite? fast read dummy >> cycle support is already there. While, >> $subject patch adds it for quad read. > The m25p80 can only handle the 8bit dummy now, such as fast read and > QOR(0x6b). > > But it can not handle the QIOR (0XEB, may needs 4bit dummy), and can > not handle the DDR QIOR(0XED, may > needs 6bit dummy). > Correct, as I explained in one of my comment on v1, that the creating of a quad api is justified for this reason itself, since there are other quad commands wth different dummy cycle requirements. We can build on top of $subject patch and try to add support for other quad commands also. ? > thanks > Huang Shijie >
于 2013年09月27日 14:27, Sourav Poddar 写道: > hmm..I think thats should be handle in your controller by checking > t->tx_buf and see what command is used. Just as Mark ever said: Parsing out the command makes the QuadSPI driver looks like a SPI NOR driver, not a SPI driver. And that's why i need to rewrite the m25p80 code. Btw: i do not object your patch, i hope it can be merged as soon as possible. thanks Huang Shijie
Hi Brian, On Thursday 26 September 2013 04:02 PM, Sourav Poddar wrote: > Some flash also support quad read mode. > Adding support for adding quad mode in m25p80. > > Signed-off-by: Sourav Poddar<sourav.poddar@ti.com> > --- > v1->v2: > - Make the code more modular > - put proper error checks > drivers/mtd/devices/m25p80.c | 155 +++++++++++++++++++++++++++++++++++++++--- > 1 files changed, 144 insertions(+), 11 deletions(-) > > diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c > index 26b14f9..3c8a794 100644 > --- a/drivers/mtd/devices/m25p80.c > +++ b/drivers/mtd/devices/m25p80.c > @@ -41,6 +41,7 @@ > #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ > #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ > #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ > +#define OPCODE_QUAD_READ 0x6b /* QUAD READ */ > #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ > #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ > #define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ > @@ -48,10 +49,12 @@ > #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ > #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ > #define OPCODE_RDID 0x9f /* Read JEDEC ID */ > +#define OPCODE_RDCR 0x35 /* Read configuration register */ > > /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ > #define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ > #define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ > +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */ > #define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ > #define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ > > @@ -76,6 +79,9 @@ > #define SR_BP2 0x10 /* Block protect 2 */ > #define SR_SRWD 0x80 /* SR write protect */ > > +/* Configuration Register bits. */ > +#define QUAD_CR_EN 0x2 /* Quad I/O */ > + > /* Define max times to check status register before we give up. */ > #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ > #define MAX_CMD_SIZE 5 > @@ -95,6 +101,7 @@ struct m25p { > u8 program_opcode; > u8 *command; > bool fast_read; > + bool quad_read; > }; > > static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) > @@ -163,6 +170,25 @@ static inline int write_disable(struct m25p *flash) > return spi_write_then_read(flash->spi,&code, 1, NULL, 0); > } > > +/* Read the configuration register, returning its value in the location > + * Return the configuration register value. > + * Returns negative if error occurred. > +*/ > +static int read_cr(struct m25p *flash) > +{ > + u8 code = OPCODE_RDCR; > + int ret; > + u8 val; > + > + ret = spi_write_then_read(flash->spi,&code, 1,&val, 1); > + if (ret< 0) { > + dev_err(&flash->spi->dev, "error %d reading CR\n", ret); > + return ret; > + } > + > + return val; > +} > + > /* > * Enable/disable 4-byte addressing mode. > */ > @@ -336,6 +362,97 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) > return 0; > } > > +/* Write status register and configuration register with 2 bytes > +* The first byte will be written to the status register, while the second byte > +* will be written to the configuration register. > +* Returns negative if error occurred. > +*/ > +static int write_sr_cr(struct m25p *flash, u16 val) > +{ > + flash->command[0] = OPCODE_WRSR; > + flash->command[1] = val& 0xff; > + flash->command[2] = (val>> 8); > + > + return spi_write(flash->spi, flash->command, 3); > +} > + > +static int quad_enable(struct m25p *flash) > +{ > + int ret; > + int quad_en = QUAD_CR_EN<< 8; > + > + write_enable(flash); > + > + ret = write_sr_cr(flash, quad_en); > + if (ret< 0) { > + dev_err(&flash->spi->dev, > + "error while writing configuration register"); > + return -EINVAL; > + } > + > + /* read back and check it */ > + ret = read_cr(flash); > + if (!(ret> 0&& (ret& QUAD_CR_EN))) { > + dev_err(&flash->spi->dev, > + "Quad bit not set"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int m25p80_quad_read(struct mtd_info *mtd, loff_t from, size_t len, > + size_t *retlen, u_char *buf) > +{ > + struct m25p *flash = mtd_to_m25p(mtd); > + struct spi_transfer t[2]; > + struct spi_message m; > + uint8_t opcode; > + > + pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), > + __func__, (u32)from, len); > + > + spi_message_init(&m); > + memset(t, 0, (sizeof(t))); > + > + t[0].tx_buf = flash->command; > + t[0].len = m25p_cmdsz(flash) + (flash->quad_read ? 1 : 0); > + spi_message_add_tail(&t[0],&m); > + > + t[1].rx_buf = buf; > + t[1].len = len; > + t[1].rx_nbits = SPI_NBITS_QUAD; > + spi_message_add_tail(&t[1],&m); > + > + mutex_lock(&flash->lock); > + > + /* Wait till previous write/erase is done. */ > + if (wait_till_ready(flash)) { > + /* REVISIT status return?? */ > + mutex_unlock(&flash->lock); > + return 1; > + } > + > + /* FIXME switch to OPCODE_QUAD_READ. It's required for higher > + * clocks; and at this writing, every chip this driver handles > + * supports that opcode. > + */ > + > + /* Set up the write data buffer. */ > + opcode = flash->read_opcode; > + flash->command[0] = opcode; > + m25p_addr2cmd(flash, from, flash->command); > + > + spi_sync(flash->spi,&m); > + > + *retlen = m.actual_length - m25p_cmdsz(flash) - > + (flash->quad_read ? 1 : 0); > + > + mutex_unlock(&flash->lock); > + > + return 0; > +} > + > /* > * Read an address range from the flash chip. The address range > * may be any size provided it is within the physical boundaries. > @@ -928,6 +1045,7 @@ static int m25p_probe(struct spi_device *spi) > unsigned i; > struct mtd_part_parser_data ppdata; > struct device_node __maybe_unused *np = spi->dev.of_node; > + int ret; > > #ifdef CONFIG_MTD_OF_PARTS > if (!of_device_is_available(np)) > @@ -979,15 +1097,9 @@ static int m25p_probe(struct spi_device *spi) > } > } > > - flash = kzalloc(sizeof *flash, GFP_KERNEL); > + flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); > if (!flash) > return -ENOMEM; > - flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0), > - GFP_KERNEL); > - if (!flash->command) { > - kfree(flash); > - return -ENOMEM; > - } > > flash->spi = spi; > mutex_init(&flash->lock); > @@ -1015,7 +1127,6 @@ static int m25p_probe(struct spi_device *spi) > flash->mtd.flags = MTD_CAP_NORFLASH; > flash->mtd.size = info->sector_size * info->n_sectors; > flash->mtd._erase = m25p80_erase; > - flash->mtd._read = m25p80_read; > > /* flash protection support for STmicro chips */ > if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { > @@ -1067,6 +1178,28 @@ static int m25p_probe(struct spi_device *spi) > > flash->program_opcode = OPCODE_PP; > > + flash->quad_read = false; > + if (spi->mode&& SPI_RX_QUAD) > + flash->quad_read = true; > + > + flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : > + (flash->quad_read ? 1 : 0)), GFP_KERNEL); > + if (!flash->command) { > + kfree(flash); > + return -ENOMEM; > + } > + > + if (flash->quad_read) { > + ret = quad_enable(flash); > + if (ret) { > + dev_err(&spi->dev, > + "error enabling quad"); > + return -EINVAL; > + } > + flash->mtd._read = m25p80_quad_read; > + } else > + flash->mtd._read = m25p80_read; > + > if (info->addr_width) > flash->addr_width = info->addr_width; > else if (flash->mtd.size> 0x1000000) { > @@ -1074,9 +1207,9 @@ static int m25p_probe(struct spi_device *spi) > flash->addr_width = 4; > if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { > /* Dedicated 4-byte command set */ > - flash->read_opcode = flash->fast_read ? > - OPCODE_FAST_READ_4B : > - OPCODE_NORM_READ_4B; > + flash->read_opcode = (flash->fast_read ? > + OPCODE_FAST_READ_4B : (flash->quad_read ? > + OPCODE_QUAD_READ_4B : OPCODE_NORM_READ_4B)); > flash->program_opcode = OPCODE_PP_4B; > /* No small sector erase for 4-byte command set */ > flash->erase_opcode = OPCODE_SE_4B; Gentle Ping on this..
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 26b14f9..3c8a794 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -41,6 +41,7 @@ #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_QUAD_READ 0x6b /* QUAD READ */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ #define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -48,10 +49,12 @@ #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */ #define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ @@ -76,6 +79,9 @@ #define SR_BP2 0x10 /* Block protect 2 */ #define SR_SRWD 0x80 /* SR write protect */ +/* Configuration Register bits. */ +#define QUAD_CR_EN 0x2 /* Quad I/O */ + /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 5 @@ -95,6 +101,7 @@ struct m25p { u8 program_opcode; u8 *command; bool fast_read; + bool quad_read; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -163,6 +170,25 @@ static inline int write_disable(struct m25p *flash) return spi_write_then_read(flash->spi, &code, 1, NULL, 0); } +/* Read the configuration register, returning its value in the location + * Return the configuration register value. + * Returns negative if error occurred. +*/ +static int read_cr(struct m25p *flash) +{ + u8 code = OPCODE_RDCR; + int ret; + u8 val; + + ret = spi_write_then_read(flash->spi, &code, 1, &val, 1); + if (ret < 0) { + dev_err(&flash->spi->dev, "error %d reading CR\n", ret); + return ret; + } + + return val; +} + /* * Enable/disable 4-byte addressing mode. */ @@ -336,6 +362,97 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; } +/* Write status register and configuration register with 2 bytes +* The first byte will be written to the status register, while the second byte +* will be written to the configuration register. +* Returns negative if error occurred. +*/ +static int write_sr_cr(struct m25p *flash, u16 val) +{ + flash->command[0] = OPCODE_WRSR; + flash->command[1] = val & 0xff; + flash->command[2] = (val >> 8); + + return spi_write(flash->spi, flash->command, 3); +} + +static int quad_enable(struct m25p *flash) +{ + int ret; + int quad_en = QUAD_CR_EN << 8; + + write_enable(flash); + + ret = write_sr_cr(flash, quad_en); + if (ret < 0) { + dev_err(&flash->spi->dev, + "error while writing configuration register"); + return -EINVAL; + } + + /* read back and check it */ + ret = read_cr(flash); + if (!(ret > 0 && (ret & QUAD_CR_EN))) { + dev_err(&flash->spi->dev, + "Quad bit not set"); + return -EINVAL; + } + + return 0; +} + +static int m25p80_quad_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct m25p *flash = mtd_to_m25p(mtd); + struct spi_transfer t[2]; + struct spi_message m; + uint8_t opcode; + + pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), + __func__, (u32)from, len); + + spi_message_init(&m); + memset(t, 0, (sizeof(t))); + + t[0].tx_buf = flash->command; + t[0].len = m25p_cmdsz(flash) + (flash->quad_read ? 1 : 0); + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + t[1].rx_nbits = SPI_NBITS_QUAD; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&flash->lock); + + /* Wait till previous write/erase is done. */ + if (wait_till_ready(flash)) { + /* REVISIT status return?? */ + mutex_unlock(&flash->lock); + return 1; + } + + /* FIXME switch to OPCODE_QUAD_READ. It's required for higher + * clocks; and at this writing, every chip this driver handles + * supports that opcode. + */ + + /* Set up the write data buffer. */ + opcode = flash->read_opcode; + flash->command[0] = opcode; + m25p_addr2cmd(flash, from, flash->command); + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - m25p_cmdsz(flash) - + (flash->quad_read ? 1 : 0); + + mutex_unlock(&flash->lock); + + return 0; +} + /* * Read an address range from the flash chip. The address range * may be any size provided it is within the physical boundaries. @@ -928,6 +1045,7 @@ static int m25p_probe(struct spi_device *spi) unsigned i; struct mtd_part_parser_data ppdata; struct device_node __maybe_unused *np = spi->dev.of_node; + int ret; #ifdef CONFIG_MTD_OF_PARTS if (!of_device_is_available(np)) @@ -979,15 +1097,9 @@ static int m25p_probe(struct spi_device *spi) } } - flash = kzalloc(sizeof *flash, GFP_KERNEL); + flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0), - GFP_KERNEL); - if (!flash->command) { - kfree(flash); - return -ENOMEM; - } flash->spi = spi; mutex_init(&flash->lock); @@ -1015,7 +1127,6 @@ static int m25p_probe(struct spi_device *spi) flash->mtd.flags = MTD_CAP_NORFLASH; flash->mtd.size = info->sector_size * info->n_sectors; flash->mtd._erase = m25p80_erase; - flash->mtd._read = m25p80_read; /* flash protection support for STmicro chips */ if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { @@ -1067,6 +1178,28 @@ static int m25p_probe(struct spi_device *spi) flash->program_opcode = OPCODE_PP; + flash->quad_read = false; + if (spi->mode && SPI_RX_QUAD) + flash->quad_read = true; + + flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : + (flash->quad_read ? 1 : 0)), GFP_KERNEL); + if (!flash->command) { + kfree(flash); + return -ENOMEM; + } + + if (flash->quad_read) { + ret = quad_enable(flash); + if (ret) { + dev_err(&spi->dev, + "error enabling quad"); + return -EINVAL; + } + flash->mtd._read = m25p80_quad_read; + } else + flash->mtd._read = m25p80_read; + if (info->addr_width) flash->addr_width = info->addr_width; else if (flash->mtd.size > 0x1000000) { @@ -1074,9 +1207,9 @@ static int m25p_probe(struct spi_device *spi) flash->addr_width = 4; if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Dedicated 4-byte command set */ - flash->read_opcode = flash->fast_read ? - OPCODE_FAST_READ_4B : - OPCODE_NORM_READ_4B; + flash->read_opcode = (flash->fast_read ? + OPCODE_FAST_READ_4B : (flash->quad_read ? + OPCODE_QUAD_READ_4B : OPCODE_NORM_READ_4B)); flash->program_opcode = OPCODE_PP_4B; /* No small sector erase for 4-byte command set */ flash->erase_opcode = OPCODE_SE_4B;
Some flash also support quad read mode. Adding support for adding quad mode in m25p80. Signed-off-by: Sourav Poddar <sourav.poddar@ti.com> --- v1->v2: - Make the code more modular - put proper error checks drivers/mtd/devices/m25p80.c | 155 +++++++++++++++++++++++++++++++++++++++--- 1 files changed, 144 insertions(+), 11 deletions(-)