From cf32b71e981ca63e8f349d8585ca2a3583b556e0 Mon Sep 17 00:00:00 2001 From: Ernst Schwab Date: Mon, 28 Jun 2010 17:49:29 -0700 Subject: spi/mmc_spi: SPI bus locking API, using mutex SPI bus locking API to allow exclusive access to the SPI bus, especially, but not limited to, for the mmc_spi driver. Coded according to an outline from Grant Likely; here is his specification (accidentally swapped function names corrected): It requires 3 things to be added to struct spi_master. - 1 Mutex - 1 spin lock - 1 flag. The mutex protects spi_sync, and provides sleeping "for free" The spinlock protects the atomic spi_async call. The flag is set when the lock is obtained, and checked while holding the spinlock in spi_async(). If the flag is checked, then spi_async() must fail immediately. The current runtime API looks like this: spi_async(struct spi_device*, struct spi_message*); spi_sync(struct spi_device*, struct spi_message*); The API needs to be extended to this: spi_async(struct spi_device*, struct spi_message*) spi_sync(struct spi_device*, struct spi_message*) spi_bus_lock(struct spi_master*) /* although struct spi_device* might be easier */ spi_bus_unlock(struct spi_master*) spi_async_locked(struct spi_device*, struct spi_message*) spi_sync_locked(struct spi_device*, struct spi_message*) Drivers can only call the last two if they already hold the spi_master_lock(). spi_bus_lock() obtains the mutex, obtains the spin lock, sets the flag, and releases the spin lock before returning. It doesn't even need to sleep while waiting for "in-flight" spi_transactions to complete because its purpose is to guarantee no additional transactions are added. It does not guarantee that the bus is idle. spi_bus_unlock() clears the flag and releases the mutex, which will wake up any waiters. The difference between spi_async() and spi_async_locked() is that the locked version bypasses the check of the lock flag. Both versions need to obtain the spinlock. The difference between spi_sync() and spi_sync_locked() is that spi_sync() must hold the mutex while enqueuing a new transfer. spi_sync_locked() doesn't because the mutex is already held. Note however that spi_sync must *not* continue to hold the mutex while waiting for the transfer to complete, otherwise only one transfer could be queued up at a time! Almost no code needs to be written. The current spi_async() and spi_sync() can probably be renamed to __spi_async() and __spi_sync() so that spi_async(), spi_sync(), spi_async_locked() and spi_sync_locked() can just become wrappers around the common code. spi_sync() is protected by a mutex because it can sleep spi_async() needs to be protected with a flag and a spinlock because it can be called atomically and must not sleep Signed-off-by: Ernst Schwab [grant.likely@secretlab.ca: use spin_lock_irqsave()] Signed-off-by: Grant Likely Tested-by: Matt Fleming Tested-by: Antonio Ospite --- include/linux/spi/spi.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index af56071b06f9..ae0a5286f558 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -262,6 +262,13 @@ struct spi_master { #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ + /* lock and mutex for SPI bus locking */ + spinlock_t bus_lock_spinlock; + struct mutex bus_lock_mutex; + + /* flag indicating that the SPI bus is locked for exclusive use */ + bool bus_lock_flag; + /* Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another @@ -542,6 +549,8 @@ static inline void spi_message_free(struct spi_message *m) extern int spi_setup(struct spi_device *spi); extern int spi_async(struct spi_device *spi, struct spi_message *message); +extern int spi_async_locked(struct spi_device *spi, + struct spi_message *message); /*---------------------------------------------------------------------------*/ @@ -551,6 +560,9 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message); */ extern int spi_sync(struct spi_device *spi, struct spi_message *message); +extern int spi_sync_locked(struct spi_device *spi, struct spi_message *message); +extern int spi_bus_lock(struct spi_master *master); +extern int spi_bus_unlock(struct spi_master *master); /** * spi_write - SPI synchronous write -- cgit v1.2.3 From 3c8e1a84fd6b984a7bce8816db2e3defc57bbfe4 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 30 Jun 2010 14:27:37 -0600 Subject: spi/spi-gpio: add support for controllers without MISO or MOSI pin There are some boards that do not strictly follow SPI standard and use only 3 wires (SCLK, MOSI or MISO, SS) for connecting some simple auxiliary chips and controls them with GPIO based 'spi controller'. In this configuration the MISO or MOSI line is missing (it is not required if the chip does not transfer any data back to host or host only reads data from chip). This patch adds support for such non-standard configuration in GPIO-based SPI controller. It has been tested in configuration without MISO pin. Reviewed-by: Kyungmin Park Signed-off-by: Marek Szyprowski Acked-by: David Brownell Signed-off-by: Grant Likely --- drivers/spi/spi_gpio.c | 101 +++++++++++++++++++++++++++++++++++-------- include/linux/spi/spi_gpio.h | 5 +++ 2 files changed, 88 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi_gpio.c index 82b480cc9637..e24a63498acb 100644 --- a/drivers/spi/spi_gpio.c +++ b/drivers/spi/spi_gpio.c @@ -167,6 +167,44 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits); } +/* + * These functions do not call setmosi or getmiso if respective flag + * (SPI_MASTER_NO_RX or SPI_MASTER_NO_TX) is set, so they are safe to + * call when such pin is not present or defined in the controller. + * A separate set of callbacks is defined to get highest possible + * speed in the generic case (when both MISO and MOSI lines are + * available), as optimiser will remove the checks when argument is + * constant. + */ + +static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + unsigned flags = spi->master->flags; + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); +} + +static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + unsigned flags = spi->master->flags; + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); +} + +static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + unsigned flags = spi->master->flags; + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); +} + +static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + unsigned flags = spi->master->flags; + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); +} + /*----------------------------------------------------------------------*/ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) @@ -232,19 +270,30 @@ static int __init spi_gpio_alloc(unsigned pin, const char *label, bool is_in) } static int __init -spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label) +spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label, + u16 *res_flags) { int value; /* NOTE: SPI_*_GPIO symbols may reference "pdata" */ - value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false); - if (value) - goto done; + if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) { + value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false); + if (value) + goto done; + } else { + /* HW configuration without MOSI pin */ + *res_flags |= SPI_MASTER_NO_TX; + } - value = spi_gpio_alloc(SPI_MISO_GPIO, label, true); - if (value) - goto free_mosi; + if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) { + value = spi_gpio_alloc(SPI_MISO_GPIO, label, true); + if (value) + goto free_mosi; + } else { + /* HW configuration without MISO pin */ + *res_flags |= SPI_MASTER_NO_RX; + } value = spi_gpio_alloc(SPI_SCK_GPIO, label, false); if (value) @@ -253,9 +302,11 @@ spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label) goto done; free_miso: - gpio_free(SPI_MISO_GPIO); + if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) + gpio_free(SPI_MISO_GPIO); free_mosi: - gpio_free(SPI_MOSI_GPIO); + if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) + gpio_free(SPI_MOSI_GPIO); done: return value; } @@ -266,6 +317,7 @@ static int __init spi_gpio_probe(struct platform_device *pdev) struct spi_master *master; struct spi_gpio *spi_gpio; struct spi_gpio_platform_data *pdata; + u16 master_flags = 0; pdata = pdev->dev.platform_data; #ifdef GENERIC_BITBANG @@ -273,7 +325,7 @@ static int __init spi_gpio_probe(struct platform_device *pdev) return -ENODEV; #endif - status = spi_gpio_request(pdata, dev_name(&pdev->dev)); + status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags); if (status < 0) return status; @@ -289,6 +341,7 @@ static int __init spi_gpio_probe(struct platform_device *pdev) if (pdata) spi_gpio->pdata = *pdata; + master->flags = master_flags; master->bus_num = pdev->id; master->num_chipselect = SPI_N_CHIPSEL; master->setup = spi_gpio_setup; @@ -296,10 +349,18 @@ static int __init spi_gpio_probe(struct platform_device *pdev) spi_gpio->bitbang.master = spi_master_get(master); spi_gpio->bitbang.chipselect = spi_gpio_chipselect; - spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; - spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1; - spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2; - spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3; + + if ((master_flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_RX)) == 0) { + spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; + spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1; + spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2; + spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3; + } else { + spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0; + spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1; + spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2; + spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3; + } spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer; spi_gpio->bitbang.flags = SPI_CS_HIGH; @@ -307,8 +368,10 @@ static int __init spi_gpio_probe(struct platform_device *pdev) if (status < 0) { spi_master_put(spi_gpio->bitbang.master); gpio_free: - gpio_free(SPI_MISO_GPIO); - gpio_free(SPI_MOSI_GPIO); + if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) + gpio_free(SPI_MISO_GPIO); + if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) + gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); spi_master_put(master); } @@ -331,8 +394,10 @@ static int __exit spi_gpio_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - gpio_free(SPI_MISO_GPIO); - gpio_free(SPI_MOSI_GPIO); + if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) + gpio_free(SPI_MISO_GPIO); + if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) + gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); return status; diff --git a/include/linux/spi/spi_gpio.h b/include/linux/spi/spi_gpio.h index ca6782ee4b9f..369b3d7d5b95 100644 --- a/include/linux/spi/spi_gpio.h +++ b/include/linux/spi/spi_gpio.h @@ -29,11 +29,16 @@ * SPI_GPIO_NO_CHIPSELECT to the controller_data: * .controller_data = (void *) SPI_GPIO_NO_CHIPSELECT; * + * If the MISO or MOSI pin is not available then it should be set to + * SPI_GPIO_NO_MISO or SPI_GPIO_NO_MOSI. + * * If the bitbanged bus is later switched to a "native" controller, * that platform_device and controller_data should be removed. */ #define SPI_GPIO_NO_CHIPSELECT ((unsigned long)-1l) +#define SPI_GPIO_NO_MISO ((unsigned long)-1l) +#define SPI_GPIO_NO_MOSI ((unsigned long)-1l) /** * struct spi_gpio_platform_data - parameter for bitbanged SPI master -- cgit v1.2.3