public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [patch 2.6.15-rc5-mm3 1/3] SPI core tweaks, bugfix
@ 2005-12-18 18:37 David Brownell
  2005-12-20  6:43 ` [patch 2.6.15-rc5-mm3] M25 series SPI flash David Brownell
  0 siblings, 1 reply; 3+ messages in thread
From: David Brownell @ 2005-12-18 18:37 UTC (permalink / raw)
  To: Andrew Morton, linux-kernel; +Cc: spi-devel-general

[-- Attachment #1: Type: text/plain, Size: 210 bytes --]

The bugfix was on the spi_unregister_master() path, various folk have
reported problems and I suspect this will resolve them.

The API tweaks are some inlined wrappers and doc updates, plus small
error checks.

[-- Attachment #2: spi-core-updates.patch --]
[-- Type: text/x-diff, Size: 12525 bytes --]

This includes various updates to the SPI core:

  - Fixes a driver model refcount bug in spi_unregister_master() paths.

  - The spi_master structures now have wrappers which help keep drivers
    from needing class-level get/put for device data or for refcounts.

  - Check for a few setup errors that would cause oopsing later.

  - Docs say more about memory management.  Highlights the use of DMA-safe
    i/o buffers, and zero-initializing spi_message and such metadata.

  - Provide a simple alloc/free for spi_message and its spi_transfer;
    this is only one of the possible memory management policies.

Nothing to break code that already works.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>

--- mm3.orig.orig/include/linux/spi/spi.h	2005-12-18 08:52:45.000000000 -0800
+++ mm3.orig/include/linux/spi/spi.h	2005-12-18 08:52:49.000000000 -0800
@@ -60,8 +60,8 @@ struct spi_device {
 	u8			mode;
 #define	SPI_CPHA	0x01			/* clock phase */
 #define	SPI_CPOL	0x02			/* clock polarity */
-#define	SPI_MODE_0	(0|0)
-#define	SPI_MODE_1	(0|SPI_CPHA)		/* (original MicroWire) */
+#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */
+#define	SPI_MODE_1	(0|SPI_CPHA)
 #define	SPI_MODE_2	(SPI_CPOL|0)
 #define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
 #define	SPI_CS_HIGH	0x04			/* chipselect active high? */
@@ -209,6 +209,30 @@ struct spi_master {
 	void			(*cleanup)(const struct spi_device *spi);
 };
 
+static inline void *spi_master_get_devdata(struct spi_master *master)
+{
+	return class_get_devdata(&master->cdev);
+}
+
+static inline void spi_master_set_devdata(struct spi_master *master, void *data)
+{
+	class_set_devdata(&master->cdev, data);
+}
+
+static inline struct spi_master *spi_master_get(struct spi_master *master)
+{
+	if (!master || !class_device_get(&master->cdev))
+		return NULL;
+	return master;
+}
+
+static inline void spi_master_put(struct spi_master *master)
+{
+	if (master)
+		class_device_put(&master->cdev);
+}
+
+
 /* the spi driver core manages memory for the spi_master classdev */
 extern struct spi_master *
 spi_alloc_master(struct device *host, unsigned size);
@@ -271,11 +295,17 @@ extern struct spi_master *spi_busnum_to_
  * stay selected until the next transfer.  This is purely a performance
  * hint; the controller driver may need to select a different device
  * for the next message.
+ *
+ * The code that submits an spi_message (and its spi_transfers)
+ * to the lower layers is responsible for managing its memory.
+ * Zero-initialize every field you don't set up explicitly, to
+ * insulate against future API updates.
  */
 struct spi_transfer {
 	/* it's ok if tx_buf == rx_buf (right?)
 	 * for MicroWire, one buffer must be null
-	 * buffers must work with dma_*map_single() calls
+	 * buffers must work with dma_*map_single() calls, unless
+	 *   spi_message.is_dma_mapped reports a pre-existing mapping
 	 */
 	const void	*tx_buf;
 	void		*rx_buf;
@@ -302,6 +332,11 @@ struct spi_transfer {
  * @status: zero for success, else negative errno
  * @queue: for use by whichever driver currently owns the message
  * @state: for use by whichever driver currently owns the message
+ *
+ * The code that submits an spi_message (and its spi_transfers)
+ * to the lower layers is responsible for managing its memory.
+ * Zero-initialize every field you don't set up explicitly, to
+ * insulate against future API updates.
  */
 struct spi_message {
 	struct spi_transfer	*transfers;
@@ -336,6 +371,29 @@ struct spi_message {
 	void			*state;
 };
 
+/* It's fine to embed message and transaction structures in other data
+ * structures so long as you don't free them while they're in use.
+ */
+
+static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags)
+{
+	struct spi_message *m;
+
+	m = kzalloc(sizeof(struct spi_message)
+			+ ntrans * sizeof(struct spi_transfer),
+			flags);
+	if (m) {
+		m->transfers = (void *)(m + 1);
+		m->n_transfer = ntrans;
+	}
+	return m;
+}
+
+static inline void spi_message_free(struct spi_message *m)
+{
+	kfree(m);
+}
+
 /**
  * spi_setup -- setup SPI mode and clock rate
  * @spi: the device whose settings are being modified
@@ -363,7 +421,10 @@ spi_setup(struct spi_device *spi)
  * The completion callback is invoked in a context which can't sleep.
  * Before that invocation, the value of message->status is undefined.
  * When the callback is issued, message->status holds either zero (to
- * indicate complete success) or a negative error code.
+ * indicate complete success) or a negative error code.  After that
+ * callback returns, the driver which issued the transfer request may
+ * deallocate the associated memory; it's no longer in use by any SPI
+ * core or controller driver code.
  *
  * Note that although all messages to a spi_device are handled in
  * FIFO order, messages may go to different devices in other orders.
@@ -445,6 +506,7 @@ spi_read(struct spi_device *spi, u8 *buf
 	return spi_sync(spi, &m);
 }
 
+/* this copies txbuf and rxbuf data; for small transfers only! */
 extern int spi_write_then_read(struct spi_device *spi,
 		const u8 *txbuf, unsigned n_tx,
 		u8 *rxbuf, unsigned n_rx);
@@ -555,8 +617,9 @@ spi_register_board_info(struct spi_board
 
 
 /* If you're hotplugging an adapter with devices (parport, usb, etc)
- * use spi_new_device() to describe each device.  You would then call
- * spi_unregister_device() to start making that device vanish.
+ * use spi_new_device() to describe each device.  You can also call
+ * spi_unregister_device() to start making that device vanish, but
+ * normally that would be handled by spi_unregister_master().
  */
 extern struct spi_device *
 spi_new_device(struct spi_master *, struct spi_board_info *);
--- mm3.orig.orig/drivers/spi/spi.c	2005-12-18 08:52:45.000000000 -0800
+++ mm3.orig/drivers/spi/spi.c	2005-12-18 10:18:13.000000000 -0800
@@ -38,7 +38,7 @@ static void spidev_release(struct device
 	if (spi->master->cleanup)
 		spi->master->cleanup(spi);
 
-	class_device_put(&spi->master->cdev);
+	spi_master_put(spi->master);
 	kfree(dev);
 }
 
@@ -90,7 +90,7 @@ static int spi_suspend(struct device *de
 	int			value;
 	struct spi_driver	*drv = to_spi_driver(dev->driver);
 
-	if (!drv || !drv->suspend)
+	if (!drv->suspend)
 		return 0;
 
 	/* suspend will stop irqs and dma; no more i/o */
@@ -105,7 +105,7 @@ static int spi_resume(struct device *dev
 	int			value;
 	struct spi_driver	*drv = to_spi_driver(dev->driver);
 
-	if (!drv || !drv->resume)
+	if (!drv->resume)
 		return 0;
 
 	/* resume may restart the i/o queue */
@@ -198,7 +198,7 @@ spi_new_device(struct spi_master *master
 
 	/* NOTE:  caller did any chip->bus_num checks necessary */
 
-	if (!class_device_get(&master->cdev))
+	if (!spi_master_get(master))
 		return NULL;
 
 	proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
@@ -244,7 +244,7 @@ spi_new_device(struct spi_master *master
 	return proxy;
 
 fail:
-	class_device_put(&master->cdev);
+	spi_master_put(master);
 	kfree(proxy);
 	return NULL;
 }
@@ -324,8 +324,6 @@ static void spi_master_release(struct cl
 	struct spi_master *master;
 
 	master = container_of(cdev, struct spi_master, cdev);
-	put_device(master->cdev.dev);
-	master->cdev.dev = NULL;
 	kfree(master);
 }
 
@@ -339,8 +337,9 @@ static struct class spi_master_class = {
 /**
  * spi_alloc_master - allocate SPI master controller
  * @dev: the controller, possibly using the platform_bus
- * @size: how much driver-private data to preallocate; a pointer to this
- * 	memory in the class_data field of the returned class_device
+ * @size: how much driver-private data to preallocate; the pointer to this
+ * 	memory is in the class_data field of the returned class_device,
+ *	accessible with spi_master_get_devdata().
  *
  * This call is used only by SPI master controller drivers, which are the
  * only ones directly touching chip registers.  It's how they allocate
@@ -350,14 +349,17 @@ static struct class spi_master_class = {
  * master structure on success, else NULL.
  *
  * The caller is responsible for assigning the bus number and initializing
- * the master's methods before calling spi_add_master(), or else (on error)
- * calling class_device_put() to prevent a memory leak.
+ * the master's methods before calling spi_add_master(); and (after errors
+ * adding the device) calling spi_master_put() to prevent a memory leak.
  */
 struct spi_master * __init_or_module
 spi_alloc_master(struct device *dev, unsigned size)
 {
 	struct spi_master	*master;
 
+	if (!dev)
+		return NULL;
+
 	master = kzalloc(size + sizeof *master, SLAB_KERNEL);
 	if (!master)
 		return NULL;
@@ -365,7 +367,7 @@ spi_alloc_master(struct device *dev, uns
 	class_device_initialize(&master->cdev);
 	master->cdev.class = &spi_master_class;
 	master->cdev.dev = get_device(dev);
-	class_set_devdata(&master->cdev, &master[1]);
+	spi_master_set_devdata(master, &master[1]);
 
 	return master;
 }
@@ -387,6 +389,8 @@ EXPORT_SYMBOL_GPL(spi_alloc_master);
  *
  * This must be called from context that can sleep.  It returns zero on
  * success, else a negative error code (dropping the master's refcount).
+ * After a successful return, the caller is responsible for calling
+ * spi_unregister_master().
  */
 int __init_or_module
 spi_register_master(struct spi_master *master)
@@ -396,6 +400,9 @@ spi_register_master(struct spi_master *m
 	int			status = -ENODEV;
 	int			dynamic = 0;
 
+	if (!dev)
+		return -ENODEV;
+
 	/* convention:  dynamically assigned bus IDs count down from the max */
 	if (master->bus_num == 0) {
 		master->bus_num = atomic_dec_return(&dyn_bus_id);
@@ -425,7 +432,7 @@ EXPORT_SYMBOL_GPL(spi_register_master);
 static int __unregister(struct device *dev, void *unused)
 {
 	/* note: before about 2.6.14-rc1 this would corrupt memory: */
-	device_unregister(dev);
+	spi_unregister_device(to_spi_device(dev));
 	return 0;
 }
 
@@ -440,8 +447,9 @@ static int __unregister(struct device *d
  */
 void spi_unregister_master(struct spi_master *master)
 {
-	class_device_unregister(&master->cdev);
 	(void) device_for_each_child(master->cdev.dev, NULL, __unregister);
+	class_device_unregister(&master->cdev);
+	master->cdev.dev = NULL;
 }
 EXPORT_SYMBOL_GPL(spi_unregister_master);
 
@@ -487,6 +495,9 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master);
  * by leaving it selected in anticipation that the next message will go
  * to the same chip.  (That may increase power usage.)
  *
+ * Also, the caller is guaranteeing that the memory associated with the
+ * message will not be freed before this call returns.
+ *
  * The return value is a negative error code if the message could not be
  * submitted, else zero.  When the value is zero, then message->status is
  * also defined:  it's the completion code for the transfer, either zero
@@ -524,9 +535,9 @@ static u8	*buf;
  * is zero for success, else a negative errno status code.
  * This call may only be used from a context that may sleep.
  *
- * Parameters to this routine are always copied using a small buffer,
- * large transfers should use use spi_{async,sync}() calls with
- * dma-safe buffers.
+ * Parameters to this routine are always copied using a small buffer;
+ * performance-sensitive or bulk transfer code should instead use
+ * spi_{async,sync}() calls with dma-safe buffers.
  */
 int spi_write_then_read(struct spi_device *spi,
 		const u8 *txbuf, unsigned n_tx,
--- mm3.orig.orig/Documentation/spi/spi-summary	2005-12-18 08:52:45.000000000 -0800
+++ mm3.orig/Documentation/spi/spi-summary	2005-12-18 08:52:49.000000000 -0800
@@ -363,6 +363,22 @@ upper boundaries might include sysfs (es
 the input layer, ALSA, networking, MTD, the character device framework,
 or other Linux subsystems.
 
+Note that there are two types of memory your driver must manage as part
+of interacting with SPI devices.
+
+  - I/O buffers use the usual Linux rules, and must be DMA-safe.
+    You'd normally allocate them from the heap or free page pool.
+    Don't use the stack, or anything that's declared "static".
+    
+  - The spi_message and spi_transfer metadata used to glue those
+    I/O buffers into a group of protocol transactions.  These can
+    be allocated anywhere it's convenient, including as part of
+    other allocate-once driver data structures.  Zero-init these.
+
+If you like, spi_message_alloc() and spi_message_free() convenience
+routines are available to allocate and zero-initialize an spi_message
+with several transfers.
+
 
 How do I write an "SPI Master Controller Driver"?
 -------------------------------------------------

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [patch 2.6.15-rc5-mm3] M25 series SPI flash
  2005-12-18 18:37 [patch 2.6.15-rc5-mm3 1/3] SPI core tweaks, bugfix David Brownell
@ 2005-12-20  6:43 ` David Brownell
  2006-01-04  1:26   ` Andrew Morton
  0 siblings, 1 reply; 3+ messages in thread
From: David Brownell @ 2005-12-20  6:43 UTC (permalink / raw)
  To: Andrew Morton, linux-kernel; +Cc: spi-devel-general, Mike Lavender

[-- Attachment #1: Type: text/plain, Size: 178 bytes --]

Here's a driver for more traditional types of SPI flash, as found
in the ST M25 series.  Contributed by Mike Lavender (thanks Mike!)
and tested on some ColdFire boards.

- Dave


[-- Attachment #2: m25p80.patch --]
[-- Type: text/x-diff, Size: 16658 bytes --]

This was originally a driver for the ST M25P80 SPI flash.   It's been
updated slightly to handle other M25P series chips.

For many of these chips, the specific type could be probed, but for now
this just requires static setup with flash_platform_data that lists the
chip type (size, format) and any default partitioning to use.

From: Mike Lavender <mike@steroidmicros.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>


--- g26.orig/drivers/mtd/devices/Makefile	2005-12-18 10:17:35.000000000 -0800
+++ g26/drivers/mtd/devices/Makefile	2005-12-18 13:00:06.000000000 -0800
@@ -24,3 +24,4 @@ obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLKMTD)	+= blkmtd.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)	+= mtd_dataflash.o
+obj-$(CONFIG_MTD_M25P80)	+= m25p80.o
--- g26.orig/drivers/mtd/devices/Kconfig	2005-12-18 10:17:35.000000000 -0800
+++ g26/drivers/mtd/devices/Kconfig	2005-12-18 13:00:06.000000000 -0800
@@ -55,6 +55,14 @@ config MTD_DATAFLASH
 	  Sometimes DataFlash chips are packaged inside MMC-format
 	  cards; at this writing, the MMC stack won't handle those.
 
+config MTD_M25P80
+	tristate "Support for M25 SPI Flash"
+	depends on MTD && SPI_MASTER && EXPERIMENTAL
+	help
+	  This enables access to ST M25P80 and similar SPI flash chips,
+	  used for program and data storage.  Set up your spi devices
+	  with the right board-specific platform data.
+
 config MTD_SLRAM
 	tristate "Uncached system RAM"
 	depends on MTD
--- g26.orig/include/linux/spi/flash.h	2005-12-18 10:17:35.000000000 -0800
+++ g26/include/linux/spi/flash.h	2005-12-18 13:00:06.000000000 -0800
@@ -8,6 +8,8 @@ struct mtd_partition;
  * @name: optional flash device name (eg, as used with mtdparts=)
  * @parts: optional array of mtd_partitions for static partitioning
  * @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
  *
  * Board init code (in arch/.../mach-xxx/board-yyy.c files) can
  * provide information about SPI flash parts (such as DataFlash) to
@@ -21,6 +23,8 @@ struct flash_platform_data {
 	struct mtd_partition *parts;
 	unsigned int	nr_parts;
 
+	char		*type;
+
 	/* we'll likely add more ... use JEDEC IDs, etc */
 };
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ g26/drivers/mtd/devices/m25p80.c	2005-12-19 13:44:11.000000000 -0800
@@ -0,0 +1,576 @@
+/*
+ * MTD SPI driver for ST M25Pxx flash chips
+ *
+ * Author: Mike Lavender, mike@steroidmicros.com
+ *
+ * Copyright (c) 2005, Intec Automation Inc.
+ *
+ * Some parts are based on lart.c by Abraham Van Der Merwe
+ *
+ * Cleaned up and generalized based on mtd_dataflash.c
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+
+#include <asm/semaphore.h>
+
+
+/* NOTE: AT 25F and SST 25LF series are very similar,
+ * but commands for sector erase and chip id differ...
+ */
+
+#define FLASH_PAGESIZE		256
+
+/* Flash opcodes. */
+#define	OPCODE_WREN		6	/* Write enable */
+#define	OPCODE_RDSR		5	/* Read status register */
+#define	OPCODE_READ		3	/* Read data bytes */
+#define	OPCODE_PP		2	/* Page program */
+#define	OPCODE_SE		0xd8	/* Sector erase */
+#define	OPCODE_RES		0xab	/* Read Electronic Signature */
+#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
+
+/* Status Register bits. */
+#define	SR_WIP			1	/* Write in progress */
+#define	SR_WEL			2	/* Write enable latch */
+#define	SR_BP0			4	/* Block protect 0 */
+#define	SR_BP1			8	/* Block protect 1 */
+#define	SR_BP2			0x10	/* Block protect 2 */
+#define	SR_SRWD			0x80	/* SR write protect */
+
+/* Define max times to check status register before we give up. */
+#define	MAX_READY_WAIT_COUNT	100000
+
+
+#ifdef CONFIG_MTD_PARTITIONS
+#define	mtd_has_partitions()	(1)
+#else
+#define	mtd_has_partitions()	(0)
+#endif
+
+/****************************************************************************/
+
+struct m25p {
+	struct spi_device	*spi;
+	struct semaphore	lock;
+	struct mtd_info		mtd;
+	unsigned		partitioned;
+	u8			command[4];
+};
+
+static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
+{
+	return container_of(mtd, struct m25p, mtd);
+}
+
+/****************************************************************************/
+
+/*
+ * Internal helper functions
+ */
+
+/*
+ * Read the status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */
+static int read_sr(struct m25p *flash)
+{
+	ssize_t retval;
+	u8 code = OPCODE_RDSR;
+	u8 val;
+
+	retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+
+	if (retval < 0) {
+		dev_err(&flash->spi->dev, "error %d reading SR\n",
+				(int) retval);
+		return retval;
+	}
+
+	return val;
+}
+
+
+/*
+ * Set write enable latch with Write Enable command.
+ * Returns negative if error occurred.
+ */
+static inline int write_enable(struct m25p *flash)
+{
+	u8	code = OPCODE_WREN;
+
+	return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+}
+
+
+/*
+ * Service routine to read status register until ready, or timeout occurs.
+ * Returns non-zero if error.
+ */
+static int wait_till_ready(struct m25p *flash)
+{
+	int count;
+	int sr;
+
+	/* one chip guarantees max 5 msec wait here after page writes,
+	 * but potentially three seconds (!) after page erase.
+	 */
+	for (count = 0; count < MAX_READY_WAIT_COUNT; count++) {
+		if ((sr = read_sr(flash)) < 0)
+			break;
+		else if (!(sr & SR_WIP))
+			return 0;
+
+		/* REVISIT sometimes sleeping would be best */
+	}
+
+	return 1;
+}
+
+
+/*
+ * Erase one sector of flash memory at offset ``offset'' which is any
+ * address within the sector which should be erased.
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int erase_sector(struct m25p *flash, u32 offset)
+{
+	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", spi->dev.bus_id,
+			__FUNCTION__, offset);
+
+	/* Wait until finished previous write command. */
+	if (wait_till_ready(flash))
+		return 1;
+
+	/* Send write enable, then erase commands. */
+	write_enable(flash);
+
+	/* Set up command buffer. */
+	flash->command[0] = OPCODE_SE;
+	flash->command[1] = offset >> 16;
+	flash->command[2] = offset >> 8;
+	flash->command[3] = offset;
+
+	spi_write(flash->spi, flash->command, sizeof(flash->command));
+
+	return 0;
+}
+
+/****************************************************************************/
+
+/*
+ * MTD implementation
+ */
+
+/*
+ * Erase an address range on the flash chip.  The address range may extend
+ * one or more erase sectors.  Return an error is there is a problem erasing.
+ */
+static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct m25p *flash = mtd_to_m25p(mtd);
+	u32 addr,len;
+
+	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %z\n", spi->dev.bus_id,
+			__FUNCTION__, "at", (u32) instr->addr, instr->len);
+
+	/* sanity checks */
+	if (instr->addr + instr->len > flash->mtd.size)
+		return -EINVAL;
+	if ((instr->addr % mtd->erasesize) != 0
+			|| (instr->len % mtd->erasesize) != 0) {
+		return -EINVAL;
+	}
+
+	addr = instr->addr;
+	len = instr->len;
+
+  	down(&flash->lock);
+
+	/* now erase those sectors */
+	while (len) {
+		if (erase_sector(flash, addr)) {
+			instr->state = MTD_ERASE_FAILED;
+			up(&flash->lock);
+			return -EIO;
+		}
+
+		addr += mtd->erasesize;
+		len -= mtd->erasesize;
+	}
+
+  	up(&flash->lock);
+
+	instr->state = MTD_ERASE_DONE;
+	mtd_erase_callback(instr);
+
+	return 0;
+}
+
+/*
+ * Read an address range from the flash chip.  The address range
+ * may be any size provided it is within the physical boundaries.
+ */
+static int m25p80_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;
+
+	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %z\n", spi->dev.bus_id,
+			__FUNCTION__, "from", (u32) from, len);
+
+	/* sanity checks */
+	if (!len)
+		return 0;
+
+	if (from + len > flash->mtd.size)
+		return -EINVAL;
+
+	down(&flash->lock);
+
+	/* Wait till previous write/erase is done. */
+	if (wait_till_ready(flash)) {
+		/* REVISIT status return?? */
+		up(&flash->lock);
+		return 1;
+	}
+
+	memset(t, 0, (sizeof t));
+
+	/* NOTE:  OPCODE_FAST_READ (if available) is faster... */
+
+	/* Set up the write data buffer. */
+	flash->command[0] = OPCODE_READ;
+	flash->command[1] = from >> 16;
+	flash->command[2] = from >> 8;
+	flash->command[3] = from;
+
+	/* Byte count starts at zero. */
+	if (retlen)
+		*retlen = 0;
+
+	t[0].tx_buf = flash->command;
+	t[0].len = sizeof(flash->command);
+
+	t[1].rx_buf = buf;
+	t[1].len = len;
+
+	m.transfers = t;
+	m.n_transfer = 2;
+
+	spi_sync(flash->spi, &m);
+
+	*retlen = m.actual_length - sizeof(flash->command);
+
+  	up(&flash->lock);
+
+	return 0;
+}
+
+/*
+ * Write an address range to the flash chip.  Data must be written in
+ * FLASH_PAGESIZE chunks.  The address range may be any size provided
+ * it is within the physical boundaries.
+ */
+static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
+	size_t *retlen, const u_char *buf)
+{
+	struct m25p *flash = mtd_to_m25p(mtd);
+	u32 page_offset, page_size;
+	struct spi_transfer t[2];
+	struct spi_message m;
+
+	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %z\n", spi->dev.bus_id,
+			__FUNCTION__, "to", (u32) to, len);
+
+	if (retlen)
+		*retlen = 0;
+
+	/* sanity checks */
+	if (!len)
+		return(0);
+
+	if (to + len > flash->mtd.size)
+		return -EINVAL;
+
+  	down(&flash->lock);
+
+	/* Wait until finished previous write command. */
+	if (wait_till_ready(flash))
+		return 1;
+
+	write_enable(flash);
+
+	memset(t, 0, (sizeof t));
+
+	/* Set up the opcode in the write buffer. */
+	flash->command[0] = OPCODE_PP;
+	flash->command[1] = to >> 16;
+	flash->command[2] = to >> 8;
+	flash->command[3] = to;
+
+	t[0].tx_buf = flash->command;
+	t[0].len = sizeof(flash->command);
+
+	m.transfers = t;
+	m.n_transfer = 2;
+
+	/* what page do we start with? */
+	page_offset = to % FLASH_PAGESIZE;
+
+	/* do all the bytes fit onto one page? */
+	if (page_offset + len <= FLASH_PAGESIZE) {
+		t[1].tx_buf = buf;
+		t[1].len = len;
+
+		spi_sync(flash->spi, &m);
+
+		*retlen = m.actual_length - sizeof(flash->command);
+	} else {
+		u32 i;
+
+		/* the size of data remaining on the first page */
+		page_size = FLASH_PAGESIZE - page_offset;
+
+		t[1].tx_buf = buf;
+		t[1].len = page_size;
+		spi_sync(flash->spi, &m);
+
+		*retlen = m.actual_length - sizeof(flash->command);
+
+		/* write everything in PAGESIZE chunks */
+		for (i = page_size; i < len; i += page_size) {
+			page_size = len - i;
+			if (page_size > FLASH_PAGESIZE)
+				page_size = FLASH_PAGESIZE;
+
+			/* write the next page to flash */
+			flash->command[1] = (to + i) >> 16;
+			flash->command[2] = (to + i) >> 8;
+			flash->command[3] = (to + i);
+
+			t[1].tx_buf = buf + i;
+			t[1].len = page_size;
+
+			wait_till_ready(flash);
+
+			write_enable(flash);
+
+			spi_sync(flash->spi, &m);
+
+			*retlen += m.actual_length - sizeof(flash->command);
+	        }
+ 	}
+
+	up(&flash->lock);
+
+	return 0;
+}
+
+
+/****************************************************************************/
+
+/*
+ * SPI device driver setup and teardown
+ */
+
+struct flash_info {
+	char		*name;
+	u8		id;
+	u16		jedec_id;
+	unsigned	sector_size;
+	unsigned	n_sectors;
+};
+
+static struct flash_info __devinitdata m25p_data [] = {
+	/* REVISIT: fill in JEDEC ids, for parts that have them */
+	{ "m25p05", 0x05, 0x0000, 32 * 1024, 2 },
+	{ "m25p10", 0x10, 0x0000, 32 * 1024, 4 },
+	{ "m25p20", 0x11, 0x0000, 64 * 1024, 4 },
+	{ "m25p40", 0x12, 0x0000, 64 * 1024, 8 },
+	{ "m25p80", 0x13, 0x0000, 64 * 1024, 16 },
+	{ "m25p16", 0x14, 0x0000, 64 * 1024, 32 },
+	{ "m25p32", 0x15, 0x0000, 64 * 1024, 64 },
+	{ "m25p64", 0x16, 0x2017, 64 * 1024, 128 },
+};
+
+/*
+ * board specific setup should have ensured the SPI clock used here
+ * matches what the READ command supports, at least until this driver
+ * understands FAST_READ (for clocks over 25 MHz).
+ */
+static int __devinit m25p_probe(struct spi_device *spi)
+{
+	struct flash_platform_data	*data;
+	struct m25p			*flash;
+	struct flash_info		*info;
+	unsigned			i;
+
+	/* Platform data helps sort out which chip type we have, as
+	 * well as how this board partitions it.
+	 */
+	data = spi->dev.platform_data;
+	if (!data || !data->type) {
+		/* FIXME some chips can identify themselves with RES
+		 * or JEDEC get-id commands.  Try them ...
+		 */
+		DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", spi->dev.bus_id);
+		return -ENODEV;
+	}
+
+	for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) {
+		if (strcmp(data->type, info->name) == 0)
+			break;
+	}
+	if (i == ARRAY_SIZE(m25p_data)) {
+		DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n",
+				spi->dev.bus_id, data->type);
+		return -ENODEV;
+	}
+
+	flash = kzalloc(sizeof *flash, SLAB_KERNEL);
+	if (!flash)
+		return -ENOMEM;
+
+	flash->spi = spi;
+	init_MUTEX(&flash->lock);
+	dev_set_drvdata(&spi->dev, flash);
+
+	if (data->name)
+		flash->mtd.name = data->name;
+	else
+		flash->mtd.name = spi->dev.bus_id;
+
+	flash->mtd.type = MTD_NORFLASH;
+	flash->mtd.flags = MTD_CAP_NORFLASH;
+	flash->mtd.size = info->sector_size * info->n_sectors;
+	flash->mtd.erasesize = info->sector_size;
+	flash->mtd.erase = m25p80_erase;
+	flash->mtd.read = m25p80_read;
+	flash->mtd.write = m25p80_write;
+
+	dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
+			flash->mtd.size / 1024);
+
+	DEBUG(MTD_DEBUG_LEVEL2,
+		"mtd .name = %s, .size = 0x%.8x (%uM) "
+			".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
+		flash->mtd.name,
+		flash->mtd.size, flash->mtd.size / (1024*1024),
+		flash->mtd.erasesize, flash->mtd.erasesize / 1024,
+		flash->mtd.numeraseregions);
+
+	if (flash->mtd.numeraseregions)
+		for (i = 0; i < flash->mtd.numeraseregions; i++)
+			DEBUG(MTD_DEBUG_LEVEL2,
+				"mtd.eraseregions[%d] = { .offset = 0x%.8x, "
+				".erasesize = 0x%.8x (%uK), "
+				".numblocks = %d }\n",
+				i, flash->mtd.eraseregions[i].offset,
+				flash->mtd.eraseregions[i].erasesize,
+				flash->mtd.eraseregions[i].erasesize / 1024,
+				flash->mtd.eraseregions[i].numblocks);
+
+
+	/* partitions should match sector boundaries; and it may be good to
+	 * use readonly partitions for writeprotected sectors (BP2..BP0).
+	 */
+	if (mtd_has_partitions()) {
+		struct mtd_partition	*parts = NULL;
+		int			nr_parts = 0;
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+		static const char *part_probes[] = { "cmdlinepart", NULL, };
+
+		nr_parts = parse_mtd_partitions(&flash->mtd,
+				part_probes, &parts, 0);
+#endif
+
+		if (nr_parts <= 0 && data && data->parts) {
+			parts = data->parts;
+			nr_parts = data->nr_parts;
+		}
+
+		if (nr_parts > 0) {
+			for (i = 0; i < data->nr_parts; i++) {
+				DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
+					"{.name = %s, .offset = 0x%.8x, "
+						".size = 0x%.8x (%uK) }\n",
+					i, data->parts[i].name,
+					data->parts[i].offset,
+					data->parts[i].size,
+					data->parts[i].size / 1024);
+			}
+			flash->partitioned = 1;
+			return add_mtd_partitions(&flash->mtd, parts, nr_parts);
+		}
+	} else if (data->nr_parts)
+		dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
+				data->nr_parts, data->name);
+
+	return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0;
+}
+
+
+static int __devexit m25p_remove(struct spi_device *spi)
+{
+	struct m25p	*flash = dev_get_drvdata(&spi->dev);
+	int		status;
+
+	/* Clean up MTD stuff. */
+	if (mtd_has_partitions() && flash->partitioned)
+		status = del_mtd_partitions(&flash->mtd);
+	else
+		status = del_mtd_device(&flash->mtd);
+	if (status == 0)
+		kfree(flash);
+	return 0;
+}
+
+
+static struct spi_driver m25p80_driver = {
+	.driver = {
+		.name	= "m25p80",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe	= m25p_probe,
+	.remove	= __devexit_p(m25p_remove),
+};
+
+
+static int m25p80_init(void)
+{
+	return spi_register_driver(&m25p80_driver);
+}
+
+
+static void m25p80_exit(void)
+{
+	spi_unregister_driver(&m25p80_driver);
+}
+
+
+module_init(m25p80_init);
+module_exit(m25p80_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Lavender");
+MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patch 2.6.15-rc5-mm3] M25 series SPI flash
  2005-12-20  6:43 ` [patch 2.6.15-rc5-mm3] M25 series SPI flash David Brownell
@ 2006-01-04  1:26   ` Andrew Morton
  0 siblings, 0 replies; 3+ messages in thread
From: Andrew Morton @ 2006-01-04  1:26 UTC (permalink / raw)
  To: David Brownell; +Cc: linux-kernel, spi-devel-general, mike

David Brownell <david-b@pacbell.net> wrote:
>
> Here's a driver for more traditional types of SPI flash, as found
> in the ST M25 series.  Contributed by Mike Lavender (thanks Mike!)
> and tested on some ColdFire boards.
> 

bah, attachments..

> This was originally a driver for the ST M25P80 SPI flash.   It's been
> updated slightly to handle other M25P series chips.
> 
> For many of these chips, the specific type could be probed, but for now
> this just requires static setup with flash_platform_data that lists the
> chip type (size, format) and any default partitioning to use.
> 
> From: Mike Lavender <mike@steroidmicros.com>
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
> 

Please put the From: at the very first line of the changelog.

Mike, please review section 11 of Documentation/SubmittingPatches and send
a Signed-off-by: for this work, thanks.


> +static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
> +	size_t *retlen, const u_char *buf)
> +{
> +	struct m25p *flash = mtd_to_m25p(mtd);
> +	u32 page_offset, page_size;
> +	struct spi_transfer t[2];
> +	struct spi_message m;
> +
> +	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %z\n", spi->dev.bus_id,
> +			__FUNCTION__, "to", (u32) to, len);
> +
> +	if (retlen)
> +		*retlen = 0;

If retlen can be NULL

> +	/* sanity checks */
> +	if (!len)
> +		return(0);
> +
> +	if (to + len > flash->mtd.size)
> +		return -EINVAL;
> +
> +  	down(&flash->lock);
> +
> +	/* Wait until finished previous write command. */
> +	if (wait_till_ready(flash))
> +		return 1;
> +
> +	write_enable(flash);
> +
> +	memset(t, 0, (sizeof t));
> +
> +	/* Set up the opcode in the write buffer. */
> +	flash->command[0] = OPCODE_PP;
> +	flash->command[1] = to >> 16;
> +	flash->command[2] = to >> 8;
> +	flash->command[3] = to;
> +
> +	t[0].tx_buf = flash->command;
> +	t[0].len = sizeof(flash->command);
> +
> +	m.transfers = t;
> +	m.n_transfer = 2;
> +
> +	/* what page do we start with? */
> +	page_offset = to % FLASH_PAGESIZE;
> +
> +	/* do all the bytes fit onto one page? */
> +	if (page_offset + len <= FLASH_PAGESIZE) {
> +		t[1].tx_buf = buf;
> +		t[1].len = len;
> +
> +		spi_sync(flash->spi, &m);
> +
> +		*retlen = m.actual_length - sizeof(flash->command);

this will oops.


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2006-01-04  1:27 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-12-18 18:37 [patch 2.6.15-rc5-mm3 1/3] SPI core tweaks, bugfix David Brownell
2005-12-20  6:43 ` [patch 2.6.15-rc5-mm3] M25 series SPI flash David Brownell
2006-01-04  1:26   ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox