Skip to content

Commit

Permalink
spi/mmc_spi: SPI bus locking API, using mutex
Browse files Browse the repository at this point in the history
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 <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>
  • Loading branch information
Ernst Schwab authored and Grant Likely committed Jun 29, 2010
1 parent 7e27d6e commit cf32b71
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 33 deletions.
225 changes: 192 additions & 33 deletions drivers/spi/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,10 @@ int spi_register_master(struct spi_master *master)
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.
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);


/*-------------------------------------------------------------------------*/

Expand All @@ -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
Expand All @@ -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)

Expand Down
12 changes: 12 additions & 0 deletions include/linux/spi/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

/*---------------------------------------------------------------------------*/

Expand All @@ -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
Expand Down

0 comments on commit cf32b71

Please sign in to comment.