diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7995474148c..e9576658ff3f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -763,4 +763,7 @@ endif # SPI_MASTER # (slave support would go here) +config SPI_DYNAMIC + def_bool ACPI || OF_DYNAMIC || SPI_SLAVE + endif # SPI diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index e0632ee1723b..f0ba5eb26128 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -422,6 +422,12 @@ static LIST_HEAD(spi_master_list); */ static DEFINE_MUTEX(board_lock); +/* + * Prevents addition of devices with same chip select and + * addition of devices below an unregistering controller. + */ +static DEFINE_MUTEX(spi_add_lock); + /** * spi_alloc_device - Allocate a new SPI device * @master: Controller to which device is connected @@ -500,7 +506,6 @@ static int spi_dev_check(struct device *dev, void *data) */ int spi_add_device(struct spi_device *spi) { - static DEFINE_MUTEX(spi_add_lock); struct spi_master *master = spi->master; struct device *dev = master->dev.parent; int status; @@ -529,6 +534,13 @@ int spi_add_device(struct spi_device *spi) goto done; } + /* Controller may unregister concurrently */ + if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && + !device_is_registered(&master->dev)) { + status = -ENODEV; + goto done; + } + if (master->cs_gpios) spi->cs_gpio = master->cs_gpios[spi->chip_select]; @@ -2070,6 +2082,10 @@ static int __unregister(struct device *dev, void *null) */ void spi_unregister_master(struct spi_master *master) { + /* Prevent addition of new devices, unregister existing ones */ + if (IS_ENABLED(CONFIG_SPI_DYNAMIC)) + mutex_lock(&spi_add_lock); + device_for_each_child(&master->dev, NULL, __unregister); if (master->queued) { @@ -2089,6 +2105,9 @@ void spi_unregister_master(struct spi_master *master) if (!devres_find(master->dev.parent, devm_spi_release_master, devm_spi_match_master, master)) put_device(&master->dev); + + if (IS_ENABLED(CONFIG_SPI_DYNAMIC)) + mutex_unlock(&spi_add_lock); } EXPORT_SYMBOL_GPL(spi_unregister_master);