aboutsummaryrefslogtreecommitdiffstats
path: root/Makefile
Commit message (Expand)AuthorAgeFilesLines
* build: it is no longer necessary to call make target/linux/clean as part of m...Felix Fietkau2012-12-021-1/+0
* Makefile: add a check to bail out early when the path to the openwrt director...Felix Fietkau2012-10-211-0/+4
* build: add back the package/cleanup step to remove the root staging dir for m...Felix Fietkau2012-10-151-1/+1
* build: rework the package/install step - collect package install lists during...Felix Fietkau2012-10-061-5/+3
* abort built in prereq target if there is no site config file for the current ...Jo-Philipp Wich2012-01-191-0/+6
* add a command for printing a cleaned up make target database - will be used t...Felix Fietkau2010-09-011-3/+8
* fix for GREP_OPTIONSFlorian Fainelli2010-07-311-0/+2
* add a "make prepare" target which builds everything up to target/compile, use...Felix Fietkau2009-05-281-0/+2
* added BUILD_LOG_DIR variable, make clean will delete the ./logs dirRalph Hempel2009-05-281-1/+1
* avoid implicit rules in even more placesFelix Fietkau2009-03-031-1/+1
* remove toolchain build directory as well in 'make dirclean'Nicolas Thill2008-12-031-1/+1
* rework parallel building to get rid of some warnings and add back support for...Felix Fietkau2008-08-161-4/+4
* make package prereq checks behave more like build prereq checks (first check ...Felix Fietkau2008-08-041-1/+12
* large improvement for parallel builds. works without V=99 now and without war...Felix Fietkau2008-06-091-2/+8
* fix package/symlinks target, will call scripts/feeds update / install introdu...Ralph Hempel2008-03-201-3/+13
* use scripts/feeds instead of scripts/feeds.sh for package/symlinksFelix Fietkau2007-12-281-1/+2
* fix make clean for unconfigured build treesFelix Fietkau2007-12-041-1/+1
* fix parallel build issuesFelix Fietkau2007-11-041-5/+7
* add autorebuild check for menuconfigFelix Fietkau2007-10-141-0/+1
* add 'make prepare' target for building the tools and toolchainFelix Fietkau2007-09-151-1/+4
* major target cleanup. it is now possible to have subtargets that can override...Felix Fietkau2007-09-081-1/+1
* clean up recursive dependency handling, use timestamp.pl again, because it sa...Felix Fietkau2007-08-301-1/+1
* build system cleanup/restructuring as described in http://lists.openwrt.org/p...Felix Fietkau2007-08-071-7/+9
* fix rootfs and init script handlingFelix Fietkau2007-07-311-1/+1
* fix make distcleanFelix Fietkau2007-07-301-2/+2
* next round of cleanup, convert target/ - make -j works now ;)Felix Fietkau2007-07-301-21/+9
* next round of build system cleanup - convert package/ to new structureFelix Fietkau2007-07-301-7/+28
* toolchain/ has been convertedFelix Fietkau2007-07-301-1/+1
* fix verbose output with dash as /bin/shFelix Fietkau2007-07-301-0/+2
* build system fixes, more cleanupFelix Fietkau2007-07-281-8/+5
* more build system cleanupFelix Fietkau2007-07-281-139/+21
* initial attempt at cleaning up subdirectory handling. tools/Makefile is now b...Felix Fietkau2007-07-281-6/+7
* add kernel_oldconfig targetFelix Fietkau2007-07-161-1/+3
* kernel: fix dependencies for .config, use selections from oldconfig when runn...Felix Fietkau2007-07-161-1/+0
* target scan needs a maxdepth of 2Felix Fietkau2007-07-161-1/+1
* set maxdepth appropriately for package scanningFelix Fietkau2007-07-151-2/+2
* rewrite of the metadata scanning - cleaner code, faster scanning, recursive s...Felix Fietkau2007-07-151-2/+5
* fix distcleanFelix Fietkau2007-07-121-1/+1
* fix docs compile targetsFelix Fietkau2007-07-101-4/+4
* Regenerate the menuconfig once symlinked packages are goneFlorian Fainelli2007-05-301-0/+1
* fix target rescan (version kernel changes)Felix Fietkau2007-05-231-4/+3
* Make UCI preconfiguration optional and disabled by defaultFelix Fietkau2007-05-051-1/+1
* fixes for a few build errors on osx without finkFelix Fietkau2007-04-291-1/+1
* revert find | xargs => find | exec changes - this is completely unnecessary a...Felix Fietkau2007-04-121-1/+1
* minor tweaks to the verbose systemMike Baker2007-04-111-3/+0
* fix targetinfo, packageinfoMike Baker2007-04-111-1/+1
* fix a few bugs with the last few commitsMike Baker2007-04-071-6/+6
* make a few commands silent and fix defconfig logicFelix Fietkau2007-04-071-7/+5
* fix typoNicolas Thill2007-04-071-1/+1
* fix package/target scanningFelix Fietkau2007-04-071-2/+2
); 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 <eschwab@online.de> [grant.likely@secretlab.ca: use spin_lock_irqsave()] Signed-off-by: Grant Likely <grant.likely@secretlab.ca> Tested-by: Matt Fleming <matt@console-pimps.org> Tested-by: Antonio Ospite <ospite@studenti.unina.it> --- drivers/spi/spi.c | 225 ++++++++++++++++++++++++++++++++++++++++------- include/linux/spi/spi.h | 12 +++ 2 files changed, 204 insertions(+), 33 deletions(-) --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -527,6 +527,10 @@ int spi_register_master(struct spi_maste dynamic = 1; } + spin_lock_init(&master->bus_lock_spinlock); + mutex_init(&master->bus_lock_mutex); + master->bus_lock_flag = 0; + /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ @@ -666,6 +670,35 @@ int spi_setup(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spi_setup); +static int __spi_async(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + + /* Half-duplex links include original MicroWire, and ones with + * only one data pin like SPI_3WIRE (switches direction) or where + * either MOSI or MISO is missing. They can also be caused by + * software limitations. + */ + if ((master->flags & SPI_MASTER_HALF_DUPLEX) + || (spi->mode & SPI_3WIRE)) { + struct spi_transfer *xfer; + unsigned flags = master->flags; + + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (xfer->rx_buf && xfer->tx_buf) + return -EINVAL; + if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) + return -EINVAL; + if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) + return -EINVAL; + } + } + + message->spi = spi; + message->status = -EINPROGRESS; + return master->transfer(spi, message); +} + /** * spi_async - asynchronous SPI transfer * @spi: device with which data will be exchanged @@ -698,33 +731,68 @@ EXPORT_SYMBOL_GPL(spi_setup); int spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; + int ret; + unsigned long flags; - /* Half-duplex links include original MicroWire, and ones with - * only one data pin like SPI_3WIRE (switches direction) or where - * either MOSI or MISO is missing. They can also be caused by - * software limitations. - */ - if ((master->flags & SPI_MASTER_HALF_DUPLEX) - || (spi->mode & SPI_3WIRE)) { - struct spi_transfer *xfer; - unsigned flags = master->flags; + spin_lock_irqsave(&master->bus_lock_spinlock, flags); - list_for_each_entry(xfer, &message->transfers, transfer_list) { - if (xfer->rx_buf && xfer->tx_buf) - return -EINVAL; - if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) - return -EINVAL; - if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) - return -EINVAL; - } - } + if (master->bus_lock_flag) + ret = -EBUSY; + else + ret = __spi_async(spi, message); - message->spi = spi; - message->status = -EINPROGRESS; - return master->transfer(spi, message); + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + + return ret; } EXPORT_SYMBOL_GPL(spi_async); +/** + * spi_async_locked - version of spi_async with exclusive bus usage + * @spi: device with which data will be exchanged + * @message: describes the data transfers, including completion callback + * Context: any (irqs may be blocked, etc) + * + * This call may be used in_irq and other contexts which can't sleep, + * as well as from task contexts which can sleep. + * + * 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. 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. + * Some device might be higher priority, or have various "hard" access + * time requirements, for example. + * + * On detection of any fault during the transfer, processing of + * the entire message is aborted, and the device is deselected. + * Until returning from the associated message completion callback, + * no other spi_message queued to that device will be processed. + * (This rule applies equally to all the synchronous transfer calls, + * which are wrappers around this core asynchronous primitive.) + */ +int spi_async_locked(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + int ret; + unsigned long flags; + + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + + ret = __spi_async(spi, message); + + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + + return ret; + +} +EXPORT_SYMBOL_GPL(spi_async_locked); + /*-------------------------------------------------------------------------*/ @@ -738,6 +806,32 @@ static void spi_complete(void *arg) complete(arg); } +static int __spi_sync(struct spi_device *spi, struct spi_message *message, + int bus_locked) +{ + DECLARE_COMPLETION_ONSTACK(done); + int status; + struct spi_master *master = spi->master; + + message->complete = spi_complete; + message->context = &done; + + if (!bus_locked) + mutex_lock(&master->bus_lock_mutex); + + status = spi_async_locked(spi, message); + + if (!bus_locked) + mutex_unlock(&master->bus_lock_mutex); + + if (status == 0) { + wait_for_completion(&done); + status = message->status; + } + message->context = NULL; + return status; +} + /** * spi_sync - blocking/synchronous SPI data transfers * @spi: device with which data will be exchanged @@ -761,21 +855,86 @@ static void spi_complete(void *arg) */ int spi_sync(struct spi_device *spi, struct spi_message *message) { - DECLARE_COMPLETION_ONSTACK(done); - int status; - - message->complete = spi_complete; - message->context = &done; - status = spi_async(spi, message); - if (status == 0) { - wait_for_completion(&done); - status = message->status; - } - message->context = NULL; - return status; + return __spi_sync(spi, message, 0); } EXPORT_SYMBOL_GPL(spi_sync); +/** + * spi_sync_locked - version of spi_sync with exclusive bus usage + * @spi: device with which data will be exchanged + * @message: describes the data transfers + * Context: can sleep + * + * This call may only be used from a context that may sleep. The sleep + * is non-interruptible, and has no timeout. Low-overhead controller + * drivers may DMA directly into and out of the message buffers. + * + * This call should be used by drivers that require exclusive access to the + * SPI bus. It has to be preceeded by a spi_bus_lock call. The SPI bus must + * be released by a spi_bus_unlock call when the exclusive access is over. + * + * It returns zero on success, else a negative error code. + */ +int spi_sync_locked(struct spi_device *spi, struct spi_message *message) +{ + return __spi_sync(spi, message, 1); +} +EXPORT_SYMBOL_GPL(spi_sync_locked); + +/** + * spi_bus_lock - obtain a lock for exclusive SPI bus usage + * @master: SPI bus master that should be locked for exclusive bus access + * Context: can sleep + * + * This call may only be used from a context that may sleep. The sleep + * is non-interruptible, and has no timeout. + * + * This call should be used by drivers that require exclusive access to the + * SPI bus. The SPI bus must be released by a spi_bus_unlock call when the + * exclusive access is over. Data transfer must be done by spi_sync_locked + * and spi_async_locked calls when the SPI bus lock is held. + * + * It returns zero on success, else a negative error code. + */ +int spi_bus_lock(struct spi_master *master) +{ + unsigned long flags; + + mutex_lock(&master->bus_lock_mutex); + + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + master->bus_lock_flag = 1; + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + + /* mutex remains locked until spi_bus_unlock is called */ + + return 0; +} +EXPORT_SYMBOL_GPL(spi_bus_lock); + +/** + * spi_bus_unlock - release the lock for exclusive SPI bus usage + * @master: SPI bus master that was locked for exclusive bus access + * Context: can sleep + * + * This call may only be used from a context that may sleep. The sleep + * is non-interruptible, and has no timeout. + * + * This call releases an SPI bus lock previously obtained by an spi_bus_lock + * call. + * + * It returns zero on success, else a negative error code. + */ +int spi_bus_unlock(struct spi_master *master) +{ + master->bus_lock_flag = 0; + + mutex_unlock(&master->bus_lock_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(spi_bus_unlock); + /* portable code must never pass more than 32 bytes */ #define SPI_BUFSIZ max(32,SMP_CACHE_BYTES) --- 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(stru 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 * */ 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