From: Ernst Schwab <[email protected]> Added code for spi_register_lock_bus. One driver can lock the bus, if the SPI master supports bus locking, or there is only one chip select in use. spi_add_device will refuse to add further SPI devices if the bus master does not support bus locking and one driver requested it.
Signed-off-by: Ernst Schwab <[email protected]> --- diff -upr a/drivers/spi/spi.c b/drivers/spi/spi.c --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -285,6 +285,18 @@ int spi_add_device(struct spi_device *sp goto done; } + /* If exclusive SPI bus access was granted to an SPI client, all + * further spi_add_device calls are abandoned. + */ + if (spi->master->exclusive && + spi->chip_select!=spi->master->bus_lock_chip_select) { + dev_err(dev, "SPI bus is locked for exclusive use " + "by chipselect %d; chipselect %d cannot be used\n", + spi->master->bus_lock_chip_select, spi->chip_select); + status = -EBUSY; + goto done; + } + /* Drivers may modify this initial i/o setup, but will * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... @@ -524,6 +536,10 @@ int spi_register_master(struct spi_maste dynamic = 1; } + /* no device has registered the master for exclusive use yet */ + master->bus_lock_chip_select = SPI_MASTER_BUS_UNLOCKED; + master->exclusive = 0; + /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ @@ -783,6 +799,96 @@ int spi_unlock_bus(struct spi_device *sp } EXPORT_SYMBOL_GPL(spi_unlock_bus); +/* REVISIT: + * Temporary solution: + * Helper functions to check if exclusive access possible. + * If all masters support the spi_lock_bus callback, these can be removed. + */ +struct count_children { + unsigned n; + struct bus_type *bus; +}; + +static int maybe_count_child(struct device *dev, void *c) +{ + struct count_children *ccp = c; + + if (dev->bus == ccp->bus) { + if (ccp->n) + return -EBUSY; + ccp->n++; + } + return 0; +} + +static int spi_lock_bus_check (struct spi_device *spi) +{ + int status; + + if (spi->master->num_chipselect > 1) { + struct count_children cc; + + cc.n = 0; + cc.bus = spi->dev.bus; + status = device_for_each_child(spi->dev.parent, &cc, + maybe_count_child); + if (status < 0) { + return status; + } + } + return 0; +} + +/** + * spi_register_lock_bus - register SPI device for use of spi_lock_bus + */ +int spi_register_lock_bus(struct spi_device *spi) +{ + if (spi->master->bus_lock_chip_select != SPI_MASTER_BUS_UNLOCKED) { + /* currently, only one device can use bus locking */ + dev_err(&spi->dev, "only one SPI device can " + "get exclusive SPI bus access\n"); + return -EBUSY; + } + + if (!spi->master->lock_bus) { + /* REVISIT: + * If all masters support the spi_lock_bus entry, + * this can be removed. + * A check for exclusive SPI bus use is done if the SPI master + * does not support bus locking. + */ + if (spi_lock_bus_check(spi) < 0) { + return -EBUSY; + } + + /* lock out all future spi_add_device calls */ + spi->master->exclusive = 1; + dev_warn(&spi->dev, "ENFORCING SPI bus stays unshared!\n"); + } + + /* Remember the chip_select for which the bus is locked. */ + spi->master->bus_lock_chip_select = spi->chip_select; + + return 0; +} +EXPORT_SYMBOL_GPL(spi_register_lock_bus); + +/** + * spi_unregister_lock_bus - unregister SPI device for use of spi_lock_bus + */ +int spi_unregister_lock_bus(struct spi_device *spi) +{ + if (spi->master->bus_lock_chip_select != spi->chip_select) { + /* unregister without a matching register */ + return -EINVAL; + } + + spi->master->bus_lock_chip_select = SPI_MASTER_BUS_UNLOCKED; + return 0; +} +EXPORT_SYMBOL_GPL(spi_unregister_lock_bus); + /** * spi_sync - blocking/synchronous SPI data transfers * @spi: device with which data will be exchanged diff -upr a/include/linux/spi/spi.h b/include/linux/spi/spi.h --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -263,6 +263,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 */ + /* chipselect of the (one) spi device that may lock the bus for + * exclusive use */ + u8 bus_lock_chip_select; +#define SPI_MASTER_BUS_UNLOCKED 0xFF /* value to indicate unlocked */ + + /* enforce that the spi bus remains unshared */ + u8 exclusive; + /* Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another @@ -555,6 +563,10 @@ extern int spi_async(struct spi_device * */ extern int spi_sync(struct spi_device *spi, struct spi_message *message); + +/* Functions to ensure exclusive access to the spi bus */ +extern int spi_register_lock_bus(struct spi_device *spi); +extern int spi_unregister_lock_bus(struct spi_device *spi); extern int spi_lock_bus(struct spi_device *spi); extern int spi_unlock_bus(struct spi_device *spi); ------------------------------------------------------------------------------ SOLARIS 10 is the OS for Data Centers - provides features such as DTrace, Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW http://p.sf.net/sfu/solaris-dev2dev _______________________________________________ spi-devel-general mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/spi-devel-general
