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/linux/spi') 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/linux/spi') 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 From 4a22b8a4ad5561436b16f5278d2f9e406ffb8705 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 10 Aug 2010 18:02:23 -0700 Subject: gpio: max730x: make pullups configurable via platformdata The gpios on the max730x chips have support for internal pullups while in input mode. This patch adds support for configuring these pullups via platform data. A new member ("input_pullup_active") to the platform data struct is introduced. A set bit in this variable activates the pullups while the respective port is in input mode. This is a compatible enhancement since unset bits lead to disables pullups which was the default in the original driver. _Note_: the 4 lowest bits in "input_pullup_active" are unused because the first 4 ports of the controller are not used, too. Signed-off-by: Marc Kleine-Budde Reviewed-by: Wolfram Sang Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/max730x.c | 22 +++++++++++++++------- include/linux/spi/max7301.h | 8 ++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'include/linux/spi') diff --git a/drivers/gpio/max730x.c b/drivers/gpio/max730x.c index 7696a5625d58..94ce773f95f8 100644 --- a/drivers/gpio/max730x.c +++ b/drivers/gpio/max730x.c @@ -54,7 +54,7 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) { struct max7301 *ts = container_of(chip, struct max7301, chip); u8 *config; - u8 offset_bits; + u8 offset_bits, pin_config; int ret; /* First 4 pins are unused in the controller */ @@ -63,12 +63,15 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) config = &ts->port_config[offset >> 2]; + if (ts->input_pullup_active & BIT(offset)) + pin_config = PIN_CONFIG_IN_PULLUP; + else + pin_config = PIN_CONFIG_IN_WO_PULLUP; + mutex_lock(&ts->lock); - /* Standard GPIO API doesn't support pull-ups, has to be extended. - * Hard-coding no pollup for now. */ *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) - | (PIN_CONFIG_IN_WO_PULLUP << offset_bits); + | (pin_config << offset_bits); ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); @@ -177,6 +180,7 @@ int __devinit __max730x_probe(struct max7301 *ts) /* Power up the chip and disable IRQ output */ ts->write(dev, 0x04, 0x01); + ts->input_pullup_active = pdata->input_pullup_active; ts->chip.label = dev->driver->name; ts->chip.direction_input = max7301_direction_input; @@ -191,13 +195,17 @@ int __devinit __max730x_probe(struct max7301 *ts) ts->chip.owner = THIS_MODULE; /* - * tristate all pins in hardware and cache the + * initialize pullups according to platform data and cache the * register values for later use. */ for (i = 1; i < 8; i++) { int j; - /* 0xAA means input with internal pullup disabled */ - ts->write(dev, 0x08 + i, 0xAA); + /* + * initialize port_config with "0xAA", which means + * input with internal pullup disabled. This is needed + * to avoid writing zeros (in the inner for loop), + * which is not allowed according to the datasheet. + */ ts->port_config[i] = 0xAA; for (j = 0; j < 4; j++) { int offset = (i - 1) * 4 + j; diff --git a/include/linux/spi/max7301.h b/include/linux/spi/max7301.h index 34af0a3477bf..bcaa2f762cc1 100644 --- a/include/linux/spi/max7301.h +++ b/include/linux/spi/max7301.h @@ -11,6 +11,7 @@ struct max7301 { struct mutex lock; u8 port_config[8]; /* field 0 is unused */ u32 out_level; /* cached output levels */ + u32 input_pullup_active; struct gpio_chip chip; struct device *dev; int (*write)(struct device *dev, unsigned int reg, unsigned int val); @@ -20,6 +21,13 @@ struct max7301 { struct max7301_platform_data { /* number assigned to the first GPIO */ unsigned base; + /* + * bitmask controlling the pullup configuration, + * + * _note_ the 4 lowest bits are unused, because the first 4 + * ports of the controller are not used, too. + */ + u32 input_pullup_active; }; extern int __max730x_remove(struct device *dev); -- cgit v1.2.3 From 5c79a5ae23e72fa12f1c7c528f62bf3ea35da0dc Mon Sep 17 00:00:00 2001 From: Ernst Schwab Date: Mon, 16 Aug 2010 15:10:11 +0200 Subject: spi.h: missing kernel-doc notation, please fix Added comments in kernel-doc notation for previously added struct fields. Signed-off-by: Ernst Schwab Acked-by: Randy Dunlap Signed-off-by: Grant Likely --- include/linux/spi/spi.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux/spi') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index ae0a5286f558..92e52a1e6af3 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -213,6 +213,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @dma_alignment: SPI controller constraint on DMA buffers alignment. * @mode_bits: flags understood by this controller driver * @flags: other constraints relevant to this driver + * @bus_lock_spinlock: spinlock for SPI bus locking + * @bus_lock_mutex: mutex for SPI bus locking + * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use * @setup: updates the device mode and clocking records used by a * device's SPI controller; protocol code may call this. This * must fail if an unrecognized or unsupported mode is requested. -- cgit v1.2.3 From 4c5f7d7a1e6cf20ad515dad8a63c0813fac5bcea Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Aug 2010 22:46:28 +0300 Subject: wl12xx: change contact person for the include file Luciano should be the contact person for the include/linux/spi/wl12xx.h file. Signed-off-by: Kalle Valo Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- include/linux/spi/wl12xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/spi') diff --git a/include/linux/spi/wl12xx.h b/include/linux/spi/wl12xx.h index a223ecbc71ef..a20bccf0b5c2 100644 --- a/include/linux/spi/wl12xx.h +++ b/include/linux/spi/wl12xx.h @@ -3,7 +3,7 @@ * * Copyright (C) 2009 Nokia Corporation * - * Contact: Kalle Valo + * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License -- cgit v1.2.3 From e3e55ff5854655d8723ad8b307f02515aecc3df5 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Tue, 7 Sep 2010 15:52:06 +0800 Subject: spi/dw_spi: clean the cs_control code commit 052dc7c45i "spi/dw_spi: conditional transfer mode change" introduced cs_control code, which has a bug by using bit offset for spi mode to set transfer mode in control register. Also it forces devices who don't need cs_control to re-configure the control registers for each spi transfer. This patch will fix them Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/dw_spi.c | 17 +++++------------ include/linux/spi/dw_spi.h | 2 ++ 2 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include/linux/spi') diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 11fbbf6fb07b..56247853c298 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -181,10 +181,6 @@ static void flush(struct dw_spi *dws) wait_till_not_busy(dws); } -static void null_cs_control(u32 command) -{ -} - static int null_writer(struct dw_spi *dws) { u8 n_bytes = dws->n_bytes; @@ -322,7 +318,7 @@ static void giveback(struct dw_spi *dws) struct spi_transfer, transfer_list); - if (!last_transfer->cs_change) + if (!last_transfer->cs_change && dws->cs_control) dws->cs_control(MRST_SPI_DEASSERT); msg->state = NULL; @@ -549,13 +545,13 @@ static void pump_transfers(unsigned long data) */ if (dws->cs_control) { if (dws->rx && dws->tx) - chip->tmode = 0x00; + chip->tmode = SPI_TMOD_TR; else if (dws->rx) - chip->tmode = 0x02; + chip->tmode = SPI_TMOD_RO; else - chip->tmode = 0x01; + chip->tmode = SPI_TMOD_TO; - cr0 &= ~(0x3 << SPI_MODE_OFFSET); + cr0 &= ~SPI_TMOD_MASK; cr0 |= (chip->tmode << SPI_TMOD_OFFSET); } @@ -704,9 +700,6 @@ static int dw_spi_setup(struct spi_device *spi) chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); if (!chip) return -ENOMEM; - - chip->cs_control = null_cs_control; - chip->enable_dma = 0; } /* diff --git a/include/linux/spi/dw_spi.h b/include/linux/spi/dw_spi.h index cc813f95a2f2..c91302f3a257 100644 --- a/include/linux/spi/dw_spi.h +++ b/include/linux/spi/dw_spi.h @@ -14,7 +14,9 @@ #define SPI_MODE_OFFSET 6 #define SPI_SCPH_OFFSET 6 #define SPI_SCOL_OFFSET 7 + #define SPI_TMOD_OFFSET 8 +#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET) #define SPI_TMOD_TR 0x0 /* xmit & recv */ #define SPI_TMOD_TO 0x1 /* xmit only */ #define SPI_TMOD_RO 0x2 /* recv only */ -- cgit v1.2.3 From c1f9a095600e07fefe64eb94eb711f410100824a Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 16 Sep 2010 13:16:02 +0200 Subject: wl12xx: make wl12xx.h common to both spi and sdio Move wl12xx.h outside of the spi-specific location, so it can be shared with both spi and sdio solutions. Update all users of spi/wl12xx.h accordingly Signed-off-by: Ohad Ben-Cohen Acked-by: Luciano Coelho Acked-by: Tony Lindgren Signed-off-by: John W. Linville --- MAINTAINERS | 2 +- arch/arm/mach-omap2/board-omap3pandora.c | 1 + arch/arm/mach-omap2/board-rx51-peripherals.c | 2 +- drivers/net/wireless/wl12xx/wl1251_sdio.c | 2 +- drivers/net/wireless/wl12xx/wl1251_spi.c | 2 +- drivers/net/wireless/wl12xx/wl1271_spi.c | 2 +- include/linux/spi/wl12xx.h | 34 ---------------------------- include/linux/wl12xx.h | 34 ++++++++++++++++++++++++++++ 8 files changed, 40 insertions(+), 39 deletions(-) delete mode 100644 include/linux/spi/wl12xx.h create mode 100644 include/linux/wl12xx.h (limited to 'include/linux/spi') diff --git a/MAINTAINERS b/MAINTAINERS index 80cea03a737e..e9aec08e575a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6324,7 +6324,7 @@ W: http://wireless.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained F: drivers/net/wireless/wl12xx/wl1271* -F: include/linux/spi/wl12xx.h +F: include/linux/wl12xx.h WL3501 WIRELESS PCMCIA CARD DRIVER M: Arnaldo Carvalho de Melo diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index c0f4f12eba54..9b62b6283c6e 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 03483920ed6e..1cf994299cbe 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c index c0b68b0a9aa8..74ba9ced5393 100644 --- a/drivers/net/wireless/wl12xx/wl1251_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include "wl1251.h" diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c index 334ded9881c0..320de79667a6 100644 --- a/drivers/net/wireless/wl12xx/wl1251_spi.c +++ b/drivers/net/wireless/wl12xx/wl1251_spi.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "wl1251.h" #include "wl1251_reg.h" diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 4cb99c541e2a..c3fdab75ad2a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include "wl1271.h" diff --git a/include/linux/spi/wl12xx.h b/include/linux/spi/wl12xx.h deleted file mode 100644 index a20bccf0b5c2..000000000000 --- a/include/linux/spi/wl12xx.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of wl12xx - * - * Copyright (C) 2009 Nokia Corporation - * - * Contact: Luciano Coelho - * - * This program 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef _LINUX_SPI_WL12XX_H -#define _LINUX_SPI_WL12XX_H - -struct wl12xx_platform_data { - void (*set_power)(bool enable); - /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ - int irq; - bool use_eeprom; -}; - -#endif diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h new file mode 100644 index 000000000000..015687a1776d --- /dev/null +++ b/include/linux/wl12xx.h @@ -0,0 +1,34 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef _LINUX_WL12XX_H +#define _LINUX_WL12XX_H + +struct wl12xx_platform_data { + void (*set_power)(bool enable); + /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ + int irq; + bool use_eeprom; +}; + +#endif -- cgit v1.2.3 From 2b9603a0d7e395fb844af90fba71448bc8019077 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Mon, 2 Aug 2010 15:52:15 +0800 Subject: spi: enable spi_board_info to be registered after spi_master Currently spi_register_board_info() has to be called before its related spi_master be registered, otherwise these board info will be just ignored. This patch will remove this order limit, it adds a global spi master list like the existing global board info listr. Whenever a board info or a spi_master is registered, the spi master list or board info list will be scanned, and a new spi device will be created if there is a master-board info match. Signed-off-by: Feng Tang Signed-off-by: Grant Likely --- drivers/spi/spi.c | 88 ++++++++++++++++++++++++++----------------------- include/linux/spi/spi.h | 3 ++ 2 files changed, 50 insertions(+), 41 deletions(-) (limited to 'include/linux/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b5a78a1f4421..0845cd4c5155 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -29,11 +29,6 @@ #include #include - -/* SPI bustype and spi_master class are registered after board init code - * provides the SPI device tables, ensuring that both are present by the - * time controller driver registration causes spi_devices to "enumerate". - */ static void spidev_release(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -202,11 +197,16 @@ EXPORT_SYMBOL_GPL(spi_register_driver); struct boardinfo { struct list_head list; - unsigned n_board_info; - struct spi_board_info board_info[0]; + struct spi_board_info board_info; }; static LIST_HEAD(board_list); +static LIST_HEAD(spi_master_list); + +/* + * Used to protect add/del opertion for board_info list and + * spi_master list, and their matching process + */ static DEFINE_MUTEX(board_lock); /** @@ -371,6 +371,20 @@ struct spi_device *spi_new_device(struct spi_master *master, } EXPORT_SYMBOL_GPL(spi_new_device); +static void spi_match_master_to_boardinfo(struct spi_master *master, + struct spi_board_info *bi) +{ + struct spi_device *dev; + + if (master->bus_num != bi->bus_num) + return; + + dev = spi_new_device(master, bi); + if (!dev) + dev_err(master->dev.parent, "can't create new device for %s\n", + bi->modalias); +} + /** * spi_register_board_info - register SPI devices for a given board * @info: array of chip descriptors @@ -393,43 +407,25 @@ EXPORT_SYMBOL_GPL(spi_new_device); int __init spi_register_board_info(struct spi_board_info const *info, unsigned n) { - struct boardinfo *bi; + struct boardinfo *bi; + int i; - bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL); + bi = kzalloc(n * sizeof(*bi), GFP_KERNEL); if (!bi) return -ENOMEM; - bi->n_board_info = n; - memcpy(bi->board_info, info, n * sizeof *info); - mutex_lock(&board_lock); - list_add_tail(&bi->list, &board_list); - mutex_unlock(&board_lock); - return 0; -} + for (i = 0; i < n; i++, bi++, info++) { + struct spi_master *master; -/* FIXME someone should add support for a __setup("spi", ...) that - * creates board info from kernel command lines - */ - -static void scan_boardinfo(struct spi_master *master) -{ - struct boardinfo *bi; - - mutex_lock(&board_lock); - list_for_each_entry(bi, &board_list, list) { - struct spi_board_info *chip = bi->board_info; - unsigned n; - - for (n = bi->n_board_info; n > 0; n--, chip++) { - if (chip->bus_num != master->bus_num) - continue; - /* NOTE: this relies on spi_new_device to - * issue diagnostics when given bogus inputs - */ - (void) spi_new_device(master, chip); - } + memcpy(&bi->board_info, info, sizeof(*info)); + mutex_lock(&board_lock); + list_add_tail(&bi->list, &board_list); + list_for_each_entry(master, &spi_master_list, list) + spi_match_master_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); } - mutex_unlock(&board_lock); + + return 0; } /*-------------------------------------------------------------------------*/ @@ -512,6 +508,7 @@ int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = master->dev.parent; + struct boardinfo *bi; int status = -ENODEV; int dynamic = 0; @@ -547,8 +544,12 @@ int spi_register_master(struct spi_master *master) dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); - /* populate children from any spi device tables */ - scan_boardinfo(master); + mutex_lock(&board_lock); + list_add_tail(&master->list, &spi_master_list); + list_for_each_entry(bi, &board_list, list) + spi_match_master_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); + status = 0; /* Register devices from the device tree */ @@ -579,7 +580,12 @@ void spi_unregister_master(struct spi_master *master) { int dummy; - dummy = device_for_each_child(&master->dev, NULL, __unregister); + mutex_lock(&board_lock); + list_del(&master->list); + mutex_unlock(&board_lock); + + dummy = device_for_each_child(master->dev.parent, &master->dev, + __unregister); device_unregister(&master->dev); } EXPORT_SYMBOL_GPL(spi_unregister_master); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 92e52a1e6af3..b4d7710bc38d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -204,6 +204,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) /** * struct spi_master - interface to SPI master controller * @dev: device interface to this driver + * @list: link with the global spi_master list * @bus_num: board-specific (and often SOC-specific) identifier for a * given SPI controller. * @num_chipselect: chipselects are used to distinguish individual @@ -238,6 +239,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) struct spi_master { struct device dev; + struct list_head list; + /* other than negative (== assign one dynamically), bus_num is fully * board-specific. usually that simplifies to being SOC-specific. * example: one SOC has three SPI controllers, numbered 0..2, -- cgit v1.2.3 From ead6db084392349ad33323b1bb2916058dd7e82b Mon Sep 17 00:00:00 2001 From: Miguel Gaio Date: Wed, 27 Oct 2010 15:33:18 -0700 Subject: gpio: add support for 74x164 serial-in/parallel-out 8-bit shift register Add support for generic 74x164 serial-in/parallel-out 8-bits shift register. This driver can be used as a GPIO output expander. [akpm@linux-foundation.org: remove unused local `refresh'] Signed-off-by: Miguel Gaio Signed-off-by: Juhos Gabor Signed-off-by: Florian Fainelli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/74x164.c | 182 +++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + include/linux/spi/74x164.h | 11 +++ 4 files changed, 202 insertions(+) create mode 100644 drivers/gpio/74x164.c create mode 100644 include/linux/spi/74x164.h (limited to 'include/linux/spi') diff --git a/drivers/gpio/74x164.c b/drivers/gpio/74x164.c new file mode 100644 index 000000000000..d91ff4c282e9 --- /dev/null +++ b/drivers/gpio/74x164.c @@ -0,0 +1,182 @@ +/* + * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver + * + * Copyright (C) 2010 Gabor Juhos + * Copyright (C) 2010 Miguel Gaio + * + * This program 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 +#include +#include +#include +#include +#include + +#define GEN_74X164_GPIO_COUNT 8 + + +struct gen_74x164_chip { + struct spi_device *spi; + struct gpio_chip gpio_chip; + struct mutex lock; + u8 port_config; +}; + +static void gen_74x164_set_value(struct gpio_chip *, unsigned, int); + +static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct gen_74x164_chip, gpio_chip); +} + +static int __gen_74x164_write_config(struct gen_74x164_chip *chip) +{ + return spi_write(chip->spi, + &chip->port_config, sizeof(chip->port_config)); +} + +static int gen_74x164_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + gen_74x164_set_value(gc, offset, val); + return 0; +} + +static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct gen_74x164_chip *chip = gpio_to_chip(gc); + int ret; + + mutex_lock(&chip->lock); + ret = (chip->port_config >> offset) & 0x1; + mutex_unlock(&chip->lock); + + return ret; +} + +static void gen_74x164_set_value(struct gpio_chip *gc, + unsigned offset, int val) +{ + struct gen_74x164_chip *chip = gpio_to_chip(gc); + + mutex_lock(&chip->lock); + if (val) + chip->port_config |= (1 << offset); + else + chip->port_config &= ~(1 << offset); + + __gen_74x164_write_config(chip); + mutex_unlock(&chip->lock); +} + +static int __devinit gen_74x164_probe(struct spi_device *spi) +{ + struct gen_74x164_chip *chip; + struct gen_74x164_chip_platform_data *pdata; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + mutex_init(&chip->lock); + + dev_set_drvdata(&spi->dev, chip); + + chip->spi = spi; + + chip->gpio_chip.label = GEN_74X164_DRIVER_NAME, + chip->gpio_chip.direction_output = gen_74x164_direction_output; + chip->gpio_chip.get = gen_74x164_get_value; + chip->gpio_chip.set = gen_74x164_set_value; + chip->gpio_chip.base = pdata->base; + chip->gpio_chip.ngpio = GEN_74X164_GPIO_COUNT; + chip->gpio_chip.can_sleep = 1; + chip->gpio_chip.dev = &spi->dev; + chip->gpio_chip.owner = THIS_MODULE; + + ret = __gen_74x164_write_config(chip); + if (ret) { + dev_err(&spi->dev, "Failed writing: %d\n", ret); + goto exit_destroy; + } + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&chip->lock); + kfree(chip); + return ret; +} + +static int gen_74x164_remove(struct spi_device *spi) +{ + struct gen_74x164_chip *chip; + int ret; + + chip = dev_get_drvdata(&spi->dev); + if (chip == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + ret = gpiochip_remove(&chip->gpio_chip); + if (!ret) { + mutex_destroy(&chip->lock); + kfree(chip); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver gen_74x164_driver = { + .driver = { + .name = GEN_74X164_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = gen_74x164_probe, + .remove = __devexit_p(gen_74x164_remove), +}; + +static int __init gen_74x164_init(void) +{ + return spi_register_driver(&gen_74x164_driver); +} +subsys_initcall(gen_74x164_init); + +static void __exit gen_74x164_exit(void) +{ + spi_unregister_driver(&gen_74x164_driver); +} +module_exit(gen_74x164_exit); + +MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Miguel Gaio "); +MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e47ef94be379..bc7b0fca6415 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -344,6 +344,14 @@ config GPIO_MC33880 SPI driver for Freescale MC33880 high-side/low-side switch. This provides GPIO interface supporting inputs and outputs. +config GPIO_74X164 + tristate "74x164 serial-in/parallel-out 8-bits shift register" + depends on SPI_MASTER + help + Platform driver for 74x164 compatible serial-in/parallel-out + 8-outputs shift registers. This driver can be used to provide access + to more gpio outputs. + comment "AC97 GPIO expanders:" config GPIO_UCB1400 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 3ff0651fd173..0c23a4dd45e5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_GPIO_MAX7301) += max7301.o obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MC33880) += mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o +obj-$(CONFIG_GPIO_74X164) += 74x164.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PL061) += pl061.o diff --git a/include/linux/spi/74x164.h b/include/linux/spi/74x164.h new file mode 100644 index 000000000000..d85c52f294a0 --- /dev/null +++ b/include/linux/spi/74x164.h @@ -0,0 +1,11 @@ +#ifndef LINUX_SPI_74X164_H +#define LINUX_SPI_74X164_H + +#define GEN_74X164_DRIVER_NAME "74x164" + +struct gen_74x164_chip_platform_data { + /* number assigned to the first GPIO */ + unsigned base; +}; + +#endif -- cgit v1.2.3