From patchwork Mon Jan 10 21:21:40 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hartley Sweeten X-Patchwork-Id: 78229 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from canuck.infradead.org (canuck.infradead.org [134.117.69.58]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 14731B70A9 for ; Tue, 11 Jan 2011 08:29:45 +1100 (EST) Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.72 #1 (Red Hat Linux)) id 1PcPBe-0004ps-Rb; Mon, 10 Jan 2011 21:21:58 +0000 Received: from mail131.messagelabs.com ([216.82.242.99]) by canuck.infradead.org with esmtps (Exim 4.72 #1 (Red Hat Linux)) id 1PcPBb-0004pJ-7x for linux-mtd@lists.infradead.org; Mon, 10 Jan 2011 21:21:56 +0000 X-VirusChecked: Checked X-Env-Sender: hartleys@visionengravers.com X-Msg-Ref: server-12.tower-131.messagelabs.com!1294694506!9398818!33 X-StarScan-Version: 6.2.9; banners=-,-,- X-Originating-IP: [216.166.12.72] Received: (qmail 25891 invoked from network); 10 Jan 2011 21:21:52 -0000 Received: from out001.collaborationhost.net (HELO out001.collaborationhost.net) (216.166.12.72) by server-12.tower-131.messagelabs.com with RC4-SHA encrypted SMTP; 10 Jan 2011 21:21:52 -0000 Received: from AUSP01VMBX24.collaborationhost.net ([10.2.12.2]) by AUSP01MHUB50.collaborationhost.net ([10.2.10.3]) with mapi; Mon, 10 Jan 2011 15:21:44 -0600 From: H Hartley Sweeten To: "linux-kernel@vger.kernel.org" , "linux-mtd@lists.infradead.org" , "spi-devel-general@lists.sourceforge.net" Date: Mon, 10 Jan 2011 15:21:40 -0600 Subject: [PATCH] mtd: sst25l: fix reads with broken spi-masters Thread-Topic: [PATCH] mtd: sst25l: fix reads with broken spi-masters Thread-Index: AcuxDF9I6KfGxxvZSoOcEo66kiFNeg== Message-ID: <0D753D10438DA54287A00B027084269764CDF4146A@AUSP01VMBX24.collaborationhost.net> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: en-US MIME-Version: 1.0 X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110110_162155_462785_65E7EF8E X-CRM114-Status: GOOD ( 24.66 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [216.82.242.99 listed in list.dnswl.org] Cc: Grant Likely , "Artem.Bityutskiy@nokia.com" , "dbrownell@users.sourceforge.net" , David Woodhouse X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Some spi masters (ep93xx) have limitations when using the SFRMOUT signal for the chip select. The SFRMOUT signal is only asserted as long as the transmit fifo contains data. As soon as the last bit is clocked into the receive fifo it gets deasserted. The chip select line to the SST25L must remain asserted for the duration of the Read cycle. If it gets deasserted the command gets aborted. To handle these broken masters, implement an alternate read method that uses the fifo_size information, passed from the platform data, to make sure the command+data message always fits into the spi fifo. This insures that the messages will always complete correctly. Signed-off-by: H Hartley Sweeten Cc: David Woodhouse Cc: David Brownell Cc: Grant Likely Cc: Artem Bityutskiy --- This patch is needed in order to read from the SPI flash on the EDB93xx series dev boards. Without it any read from the device will return 0x00. diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index c163e61..864bd20 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -54,6 +54,10 @@ struct sst25l_flash { struct mtd_info mtd; int partitioned; + + bool use_fifo; + int fifo_size; + unsigned char *bounce_buf; }; struct flash_info { @@ -269,6 +273,79 @@ static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len, return 0; } +static int sst25l_read_use_fifo(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, unsigned char *buf) +{ + struct sst25l_flash *flash = to_sst25l_flash(mtd); + struct spi_message message; + struct spi_transfer transfer; + loff_t addr; + size_t count; + int xfer_size = flash->fifo_size - 4; + int ret, i; + + /* Sanity checking */ + if (len == 0) + return 0; + + if (from + len > flash->mtd.size) + return -EINVAL; + + if (retlen) + *retlen = 0; + + mutex_lock(&flash->lock); + + /* Wait for previous write/erase to complete */ + ret = sst25l_wait_till_ready(flash); + if (ret) { + mutex_unlock(&flash->lock); + return ret; + } + + for (addr = from, count = 0; addr < from + len; addr += xfer_size) { + spi_message_init(&message); + memset(&transfer, 0, sizeof(transfer)); + + /* Fill the bounce buffer with known data */ + memset(flash->bounce_buf, 0xff, flash->fifo_size); + + /* + * Send the 4 byte Read command and read back as much + * data as possible (fifo_size - 4) into the bounce buffer. + */ + flash->bounce_buf[0] = SST25L_CMD_READ; + flash->bounce_buf[1] = addr >> 16; + flash->bounce_buf[2] = addr >> 8; + flash->bounce_buf[3] = addr; + + transfer.tx_buf = flash->bounce_buf; + transfer.rx_buf = flash->bounce_buf; + transfer.len = flash->fifo_size; + spi_message_add_tail(&transfer, &message); + ret = spi_sync(flash->spi, &message); + if (ret < 0) { + mutex_unlock(&flash->lock); + return ret; + } + + /* + * Copy the read data from the bounce buffer to the + * passed buffer. Don't overflow the passed buffer. + */ + for (i = 0; i < xfer_size; i++) { + if (count < len) + buf[count++] = flash->bounce_buf[4+i]; + } + } + + if (retlen) + *retlen += count; + + mutex_unlock(&flash->lock); + return 0; +} + static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const unsigned char *buf) { @@ -400,13 +477,25 @@ static int __devinit sst25l_probe(struct spi_device *spi) else flash->mtd.name = dev_name(&spi->dev); + flash->use_fifo = data->use_fifo; + flash->fifo_size = data->fifo_size; + flash->mtd.type = MTD_NORFLASH; flash->mtd.flags = MTD_CAP_NORFLASH; flash->mtd.erasesize = flash_info->erase_size; flash->mtd.writesize = flash_info->page_size; flash->mtd.size = flash_info->page_size * flash_info->nr_pages; flash->mtd.erase = sst25l_erase; - flash->mtd.read = sst25l_read; + if (flash->use_fifo) { + flash->bounce_buf = kzalloc(flash->fifo_size, GFP_KERNEL); + if (!flash->bounce_buf) { + ret = -ENOMEM; + goto err; + } + flash->mtd.read = sst25l_read_use_fifo; + } else { + flash->mtd.read = sst25l_read; + } flash->mtd.write = sst25l_write; dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name, @@ -461,12 +550,17 @@ static int __devinit sst25l_probe(struct spi_device *spi) ret = add_mtd_device(&flash->mtd); if (ret == 1) { - kfree(flash); - dev_set_drvdata(&spi->dev, NULL); - return -ENODEV; + ret = -ENODEV; + goto err; } return 0; + +err: + kfree(flash->bounce_buf); + kfree(flash); + dev_set_drvdata(&spi->dev, NULL); + return ret; } static int __exit sst25l_remove(struct spi_device *spi) @@ -478,8 +572,10 @@ static int __exit sst25l_remove(struct spi_device *spi) ret = del_mtd_partitions(&flash->mtd); else ret = del_mtd_device(&flash->mtd); - if (ret == 0) + if (ret == 0) { + kfree(flash->bounce_buf); kfree(flash); + } return ret; } diff --git a/include/linux/spi/flash.h b/include/linux/spi/flash.h index 3f22932..8524221 100644 --- a/include/linux/spi/flash.h +++ b/include/linux/spi/flash.h @@ -10,6 +10,10 @@ struct mtd_partition; * @nr_parts: number of mtd_partitions for static partitoning * @type: optional flash device type (e.g. m25p80 vs m25p64), for use * with chips that can't be queried for JEDEC or other IDs + * @use_fifo: optional flag to read/write data using the SPI master's + * fifo limitations (for broken SPI masters that deassert the + * chip select during multi-part transfers) + * @fifo_size: optional fifo size used with broken SPI masters * * Board init code (in arch/.../mach-xxx/board-yyy.c files) can * provide information about SPI flash parts (such as DataFlash) to @@ -25,6 +29,9 @@ struct flash_platform_data { char *type; + bool use_fifo; + int fifo_size; + /* we'll likely add more ... use JEDEC IDs, etc */ };