lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <0D753D10438DA54287A00B027084269764CDF4146A@AUSP01VMBX24.collaborationhost.net>
Date:	Mon, 10 Jan 2011 15:21:40 -0600
From:	H Hartley Sweeten <hartleys@...ionengravers.com>
To:	"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
	"linux-mtd@...ts.infradead.org" <linux-mtd@...ts.infradead.org>,
	"spi-devel-general@...ts.sourceforge.net" 
	<spi-devel-general@...ts.sourceforge.net>
CC:	David Woodhouse <dwmw2@...radead.org>,
	"dbrownell@...rs.sourceforge.net" <dbrownell@...rs.sourceforge.net>,
	Grant Likely <grant.likely@...retlab.ca>,
	"Artem.Bityutskiy@...ia.com" <Artem.Bityutskiy@...ia.com>
Subject: [PATCH] mtd: sst25l: fix reads with broken spi-masters

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 <hsweeten@...ionengravers.com>
Cc: David Woodhouse <dwmw2@...radead.org>
Cc: David Brownell <dbrownell@...rs.sourceforge.net>
Cc: Grant Likely <grant.likely@...retlab.ca>
Cc: Artem Bityutskiy <Artem.Bityutskiy@...ia.com>

---

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 */
 };
 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ