Skip to contentSkip to conversation

SPI core tweaks, bugfix

PATCH New David Brownell 2 participants 3 messages Dec 18, 2005 – Jan 4, 2006

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/3 SPI core tweaks, bugfix

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.

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.

include/linux/spi/spi.h +69 -6
@@ -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 *);
drivers/spi/spi.c +28 -17

142 lines not on this page · raw message

Documentation/spi/spi-summary +16 -0
@@ -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"?
 -------------------------------------------------
#1 lore raw

PATCH 1/1 M25 series SPI flash

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

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 <[email protected]>

drivers/mtd/devices/Makefile +1 -0
@@ -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
drivers/mtd/devices/Kconfig +8 -0
@@ -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
include/linux/spi/flash.h +4 -0
@@ -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 */
 };
 
drivers/mtd/devices/m25p80.c new file +576 -0

576 lines not on this page · raw message

#2 lore raw

Re: [patch 2.6.15-rc5-mm3] M25 series SPI flash

Quoted 5 lines · David Brownell

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..

Quoted 10 lines

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 <[email protected]>

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.

drivers/mtd/devices/m25p80.c line 304

Quoted 13 lines
drivers/mtd/devices/m25p80.c new file +13 -0
+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

Quoted 30 lines
drivers/mtd/devices/m25p80.c new file +30 -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? */

drivers/mtd/devices/m25p80.c line 345

Quoted 10 lines
drivers/mtd/devices/m25p80.c new file +10 -0
+	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.