From patchwork Wed Feb 17 18:17:09 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ernst Schwab X-Patchwork-Id: 80001 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1HIHW04025600 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 17 Feb 2010 18:18:08 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-4.v29.ch3.sourceforge.com) by sfs-ml-4.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1NhoSo-0001MP-Rg; Wed, 17 Feb 2010 18:17:30 +0000 Received: from sfi-mx-1.v28.ch3.sourceforge.com ([172.29.28.121] helo=mx.sourceforge.net) by sfs-ml-4.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1NhoSn-0001MJ-60 for spi-devel-general@lists.sourceforge.net; Wed, 17 Feb 2010 18:17:29 +0000 X-ACL-Warn: Received: from moutng.kundenserver.de ([212.227.126.187]) by sfi-mx-1.v28.ch3.sourceforge.com with esmtp (Exim 4.69) id 1NhoSl-00032B-Sq; Wed, 17 Feb 2010 18:17:29 +0000 Received: from ip065 (koln-5d811195.pool.mediaWays.net [93.129.17.149]) by mrelayeu.kundenserver.de (node=mreu1) with ESMTP (Nemesis) id 0MWORa-1NBOAK18xO-00Xiqi; Wed, 17 Feb 2010 19:17:07 +0100 Date: Wed, 17 Feb 2010 19:17:09 +0100 From: Ernst Schwab To: Grant Likely , Kumar Gala , David Brownell Message-Id: <20100217191709.2fd1028c.eschwab@online.de> In-Reply-To: <20100217191513.52392dd6.eschwab@online.de> References: <20100217191513.52392dd6.eschwab@online.de> X-Mailer: Sylpheed 3.0.0beta8 (GTK+ 2.10.14; i686-pc-mingw32) Mime-Version: 1.0 X-Provags-ID: V01U2FsdGVkX19l6eioWamcdExu4IiXYKTxKtj5W3IY/u32roE UFDv1MwUbB6lVxurQqnQmKI2EIOgNTGRU1zcIdXYWdpQ8BoArZ WUWIkMvKvdEE3kfite7qvcFT4yRyy1p X-Spam-Score: 0.1 (/) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 SPF_HELO_PASS SPF: HELO matches SPF record 0.1 AWL AWL: From: address is in the auto white-list X-Headers-End: 1NhoSl-00032B-Sq Cc: spi-devel-general@lists.sourceforge.net, yi.li@analog.com, vapier@gentoo.org Subject: [spi-devel-general] [PATCH 1/2] spi/mmc_spi: SPI bus locking API, using mutex X-BeenThere: spi-devel-general@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux SPI core/device drivers discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: spi-devel-general-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Wed, 17 Feb 2010 18:18:08 +0000 (UTC) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -524,6 +524,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_chipselect = SPI_MASTER_BUS_UNLOCKED; + /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ @@ -692,7 +696,7 @@ EXPORT_SYMBOL_GPL(spi_setup); * (This rule applies equally to all the synchronous transfer calls, * which are wrappers around this core asynchronous primitive.) */ -int spi_async(struct spi_device *spi, struct spi_message *message) +static int __spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; @@ -720,7 +724,6 @@ int spi_async(struct spi_device *spi, struct spi_message *message) message->status = -EINPROGRESS; return master->transfer(spi, message); } -EXPORT_SYMBOL_GPL(spi_async); /*-------------------------------------------------------------------------*/ @@ -756,14 +759,50 @@ static void spi_complete(void *arg) * * It returns zero on success, else a negative error code. */ + +int spi_sync_locked(struct spi_device *spi, struct spi_message *message) +{ + DECLARE_COMPLETION_ONSTACK(done); + int status; + struct spi_master *master = spi->master; + + message->complete = spi_complete; + message->context = &done; + + spin_lock(&master->bus_lock_spinlock); + + status = __spi_async(spi, message); + + spin_unlock(&master->bus_lock_spinlock); + + if (status == 0) { + wait_for_completion(&done); + status = message->status; + } + + message->context = NULL; + + return status; +} +EXPORT_SYMBOL_GPL(spi_sync_locked); + int spi_sync(struct spi_device *spi, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; + struct spi_master *master = spi->master; message->complete = spi_complete; message->context = &done; - status = spi_async(spi, message); + + mutex_lock(&master->bus_lock_mutex); + + spin_lock(&master->bus_lock_spinlock); + status = __spi_async(spi, message); + spin_unlock(&master->bus_lock_spinlock); + + mutex_unlock(&master->bus_lock_mutex); + if (status == 0) { wait_for_completion(&done); status = message->status; @@ -773,6 +812,82 @@ int spi_sync(struct spi_device *spi, struct spi_message *message) } EXPORT_SYMBOL_GPL(spi_sync); +int spi_async(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + unsigned long flags; + int ret; + + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + + if (master->bus_lock_chipselect == SPI_MASTER_BUS_UNLOCKED + || master->bus_lock_chipselect == spi->chip_select) { + ret = __spi_async(spi, message); + } else { + /* REVISIT: + * if the bus is locked to an other device, message transfer + * fails. Maybe messages should be queued in the SPI layer + * and be transmitted after the lock is released. + */ + ret = -EBUSY; + } + + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(spi_async); + +int spi_async_locked(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + unsigned long flags; + int ret; + + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + + if (master->bus_lock_chipselect == spi->chip_select) { + ret = __spi_async(spi, message); + } else { + /* API error: the SPI bus lock is not held by the caller */ + ret = -EINVAL; + } + + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + + return ret; + +} +EXPORT_SYMBOL_GPL(spi_async_locked); + +int spi_bus_lock(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + + spin_lock(&master->bus_lock_spinlock); + mutex_lock(&master->bus_lock_mutex); + master->bus_lock_chipselect = spi->chip_select; + spin_unlock(&master->bus_lock_spinlock); + + /* mutex remains locked until spi_bus_unlock is called */ + + return 0; +} +EXPORT_SYMBOL_GPL(spi_bus_lock); + +int spi_bus_unlock(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + + spin_lock(&master->bus_lock_spinlock); + mutex_unlock(&master->bus_lock_mutex); + master->bus_lock_chipselect = SPI_MASTER_BUS_UNLOCKED; + spin_unlock(&master->bus_lock_spinlock); + + 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) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -261,6 +261,14 @@ 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; + + /* chipselect of the spi device that locked the bus for exclusive use */ + u8 bus_lock_chipselect; +#define SPI_MASTER_BUS_UNLOCKED 0xFF /* value to indicate unlocked */ + /* Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another @@ -541,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); /*---------------------------------------------------------------------------*/ @@ -550,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_device *spi); +extern int spi_bus_unlock(struct spi_device *spi); /** * spi_write - SPI synchronous write