[PATCH] 3/5: Updates to SPI and mmc_spi: tx_default, kernel 2.6.19

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



(Please CC me on replies, I'm not subscribed to LKML or the SPI list.  Thanks.)

The SD/MMC SPI-based protocol isn't really duplex.  In the
normal case there's either information transmitted or received,
not both simultaneously.  The unused data is always 0xff; ones.
Unfortunately, the SPI framework leaves outgoing data for a
left-out tx_buf just as "undefined" with no other way to send
constant data than to actually pass a buffer full of ones.  I
imagine other SPI-based protocols being in the same situation so
here's a feature implementation: a controller can say it
supports a default value for the bit being shifted out whenever
there's no tx_buf.  I measured it to improve read performance by
3% for SD/MMC SPI (timing "dd" of the MMC block device) for a
DMA-based controller.  As with the cs_interactive patch, the
functionality is optional for a driver/controller, but I include
an update for the spi_bitbang framework, for clients that don't
implement their own bitbang->txrx_bufs function.  I don't expect
it to save much performance for bitbanged transfers (though the
lessened memory and cache footprint might be nice); it's mostly
to provide an example implementation.  (To make sure, I did
measure it for spi_crisv32_gpio and found no measurable
difference; less than .5% difference with same difference
between equal samples).

Tested together with the other patches on the spi_crisv32_sser
and spi_crisv32_gpio drivers (not yet in the kernel, will IIUC
be submitted as part of the usual arch-maintainer-pushes).

This patch is intended to be applied on top of the cs_inactive
patch (the previous one, 2/5).

Signed-off-by: Hans-Peter Nilsson <[email protected]>

diff -upr a/include/linux/spi/spi.h b/include/linux/spi/spi.h
--- a/include/linux/spi/spi.h	2007-01-24 07:10:09.000000000 +0100
+++ b/include/linux/spi/spi.h	2007-01-24 03:58:32.000000000 +0100
@@ -153,6 +153,9 @@ static inline void spi_unregister_driver
  *	SPI slaves, and are numbered from zero to num_chipselects.
  *	each slave has a chipselect signal, but it's common that not
  *	every chipselect is connected to a slave.
+ * @can_tx_default: the controller can specify the "default" bit value:
+ *	if this bit is set and there's no tx data in a message,
+ *	spi_transfer.tx_default then controls the data shifted out.
  * @can_cs_inactive: the controller can send data (or at least toggle the
  *	clock with undefined data output) while having chipselect inactive.
  * @setup: updates the device mode and clocking records used by a
@@ -187,6 +190,11 @@ struct spi_master {
 	 */
 	u16			num_chipselect;
 
+	/* clients can check this to see if they can avoid passing
+	 * transfers where the tx buffer is just zero or is just ones.
+	 */
+	unsigned		can_tx_default:1;
+
 	/* clients that want to toggle the clock while chipselect is
 	 * inactive (has different polarity compared to when data is
 	 * output) must test this bit.
@@ -296,6 +304,9 @@ extern struct spi_master *spi_busnum_to_
  *	important information, and it's ok to pass NULL for both tx_buf
  *	and rx_buf.  It is an error to set this bit if spi_device
  *	.can_cs_inactive == 0.
+ * @tx_default: tx data value (0 or 1) in absence of tx_buf, *iff* the
+ *      master controller supports it (see spi_master.can_tx_default),
+ *      otherwise the tx data is undefined and this bit is unused.
  * @delay_usecs: microseconds to delay after this transfer before
  *	(optionally) changing the chipselect status, then starting
  *	the next transfer or completing this spi_message.
@@ -308,7 +319,9 @@ extern struct spi_master *spi_busnum_to_
  * underlying driver uses dma.
  *
  * If the transmit buffer is null, undefined data will be shifted out
- * while filling rx_buf.  If the receive buffer is null, the data
+ * while filling rx_buf, unless the master controller has indicated
+ * can_tx_default = 1, in which case the data shifted out is specified by
+ * spi_message.tx_default.  If the receive buffer is null, the data
  * shifted in will be discarded.  Only "len" bytes shift out (or in).
  * It's an error to try to shift out a partial word.  (For example, by
  * shifting out three bytes with word size of sixteen or twenty bits;
@@ -351,6 +364,7 @@ struct spi_transfer {
 
 	unsigned	cs_change:1;
 	unsigned	cs_inactive:1;
+	unsigned	tx_default:1;
 	u8		bits_per_word;
 	u16		delay_usecs;
 	u32		speed_hz;
diff -upr a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c
--- a/drivers/spi/spi_bitbang.c	2007-01-24 07:14:12.000000000 +0100
+++ b/drivers/spi/spi_bitbang.c	2007-01-24 06:15:56.000000000 +0100
@@ -77,6 +77,8 @@ static unsigned bitbang_txrx_8(
 
 		if (tx)
 			word = *tx++;
+		else if (t->tx_default)
+			word = 0xff;
 		word = txrx_word(spi, ns, word, bits);
 		if (rx)
 			*rx++ = word;
@@ -103,6 +105,8 @@ static unsigned bitbang_txrx_16(
 
 		if (tx)
 			word = *tx++;
+		else if (t->tx_default)
+			word = 0xffff;
 		word = txrx_word(spi, ns, word, bits);
 		if (rx)
 			*rx++ = word;
@@ -129,6 +133,8 @@ static unsigned bitbang_txrx_32(
 
 		if (tx)
 			word = *tx++;
+		else if (t->tx_default)
+			word = 0xffffffffu;
 		word = txrx_word(spi, ns, word, bits);
 		if (rx)
 			*rx++ = word;
@@ -305,6 +311,7 @@ static void bitbang_work(void *_bitbang)
 		list_for_each_entry (t, &m->transfers, transfer_list) {
 			cs_inactive = t->cs_inactive;
 			BUG_ON(cs_inactive && !spi->master->can_cs_inactive);
+			BUG_ON(t->tx_default && !spi->master->can_tx_default);
 
 			if (bitbang->shutdown) {
 				status = -ESHUTDOWN;
@@ -481,6 +488,17 @@ int spi_bitbang_start(struct spi_bitbang
 	if (!bitbang->txrx_bufs) {
 		bitbang->use_dma = 0;
 		bitbang->txrx_bufs = spi_bitbang_bufs;
+
+		/*
+		 * Because we use spi_bitbang_bufs, spi_bitbang_setup
+		 * must been called (at least from the function
+		 * overriding it) to set cs->txrx_bufs, so the
+		 * function looping over the transfer bytes must be
+		 * one of bitbang_txrx_(8|16|32) where we implement
+		 * tx_default.
+		 */
+		bitbang->master->can_tx_default = 1;
+
 		if (!bitbang->master->setup) {
 			if (!bitbang->setup_transfer)
 				bitbang->setup_transfer =


brgds, H-P
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux