From patchwork Thu Sep 26 10:32:45 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Poddar, Sourav" X-Patchwork-Id: 278153 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from casper.infradead.org (unknown [IPv6:2001:770:15f::2]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 53C1D2C00C5 for ; Thu, 26 Sep 2013 20:34:21 +1000 (EST) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VP8tX-0005hO-Sf; Thu, 26 Sep 2013 10:34:04 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VP8tW-0002I0-2K; Thu, 26 Sep 2013 10:34:02 +0000 Received: from arroyo.ext.ti.com ([192.94.94.40]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VP8tT-0002HV-Ho for linux-mtd@lists.infradead.org; Thu, 26 Sep 2013 10:34:00 +0000 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id r8QAWnCX014606; Thu, 26 Sep 2013 05:32:49 -0500 Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id r8QAWn40029370; Thu, 26 Sep 2013 05:32:49 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.2.342.3; Thu, 26 Sep 2013 05:32:49 -0500 Received: from a0131647.apr.dhcp.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id r8QAWkIr025636; Thu, 26 Sep 2013 05:32:47 -0500 From: Sourav Poddar To: , , , , Subject: [PATCHv2] drivers: mtd: devices: Add quad read support. Date: Thu, 26 Sep 2013 16:02:45 +0530 Message-ID: <1380191565-28640-1-git-send-email-sourav.poddar@ti.com> X-Mailer: git-send-email 1.7.1 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130926_063359_704794_546DAC4F X-CRM114-Status: GOOD ( 22.68 ) X-Spam-Score: -9.3 (---------) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-9.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [192.94.94.40 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -2.4 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Sourav Poddar , broonie@kernel.org, balbi@ti.com X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Some flash also support quad read mode. Adding support for adding quad mode in m25p80. Signed-off-by: Sourav Poddar --- 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;