|
@@ -40,9 +40,13 @@
|
|
#include <linux/ioport.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/highmem.h>
|
|
|
|
+#include <linux/idr.h>
|
|
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/spi.h>
|
|
#include <trace/events/spi.h>
|
|
|
|
+#define SPI_DYN_FIRST_BUS_NUM 0
|
|
|
|
+
|
|
|
|
+static DEFINE_IDR(spi_master_idr);
|
|
|
|
|
|
static void spidev_release(struct device *dev)
|
|
static void spidev_release(struct device *dev)
|
|
{
|
|
{
|
|
@@ -420,6 +424,7 @@ static LIST_HEAD(spi_controller_list);
|
|
/*
|
|
/*
|
|
* Used to protect add/del opertion for board_info list and
|
|
* Used to protect add/del opertion for board_info list and
|
|
* spi_controller list, and their matching process
|
|
* spi_controller list, and their matching process
|
|
|
|
+ * also used to protect object of type struct idr
|
|
*/
|
|
*/
|
|
static DEFINE_MUTEX(board_lock);
|
|
static DEFINE_MUTEX(board_lock);
|
|
|
|
|
|
@@ -2051,11 +2056,10 @@ static int of_spi_register_master(struct spi_controller *ctlr)
|
|
*/
|
|
*/
|
|
int spi_register_controller(struct spi_controller *ctlr)
|
|
int spi_register_controller(struct spi_controller *ctlr)
|
|
{
|
|
{
|
|
- static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
|
|
|
|
struct device *dev = ctlr->dev.parent;
|
|
struct device *dev = ctlr->dev.parent;
|
|
struct boardinfo *bi;
|
|
struct boardinfo *bi;
|
|
int status = -ENODEV;
|
|
int status = -ENODEV;
|
|
- int dynamic = 0;
|
|
|
|
|
|
+ int id;
|
|
|
|
|
|
if (!dev)
|
|
if (!dev)
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
@@ -2071,17 +2075,29 @@ int spi_register_controller(struct spi_controller *ctlr)
|
|
*/
|
|
*/
|
|
if (ctlr->num_chipselect == 0)
|
|
if (ctlr->num_chipselect == 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
-
|
|
|
|
- if ((ctlr->bus_num < 0) && ctlr->dev.of_node)
|
|
|
|
- ctlr->bus_num = of_alias_get_id(ctlr->dev.of_node, "spi");
|
|
|
|
-
|
|
|
|
- /* convention: dynamically assigned bus IDs count down from the max */
|
|
|
|
|
|
+
|
|
|
|
+ /* allocate dynamic bus number using Linux idr */
|
|
|
|
+ if ((ctlr->bus_num < 0) && ctlr->dev.of_node) {
|
|
|
|
+ id = of_alias_get_id(ctlr->dev.of_node, "spi");
|
|
|
|
+ if (id >= 0) {
|
|
|
|
+ ctlr->bus_num = id;
|
|
|
|
+ mutex_lock(&board_lock);
|
|
|
|
+ id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
|
|
|
|
+ ctlr->bus_num + 1, GFP_KERNEL);
|
|
|
|
+ mutex_unlock(&board_lock);
|
|
|
|
+ if (WARN(id < 0, "couldn't get idr"))
|
|
|
|
+ return id == -ENOSPC ? -EBUSY : id;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
if (ctlr->bus_num < 0) {
|
|
if (ctlr->bus_num < 0) {
|
|
- /* FIXME switch to an IDR based scheme, something like
|
|
|
|
- * I2C now uses, so we can't run out of "dynamic" IDs
|
|
|
|
- */
|
|
|
|
- ctlr->bus_num = atomic_dec_return(&dyn_bus_id);
|
|
|
|
- dynamic = 1;
|
|
|
|
|
|
+ mutex_lock(&board_lock);
|
|
|
|
+ id = idr_alloc(&spi_master_idr, ctlr,
|
|
|
|
+ SPI_DYN_FIRST_BUS_NUM, 0, GFP_KERNEL);
|
|
|
|
+ mutex_unlock(&board_lock);
|
|
|
|
+ if (WARN(id < 0, "couldn't get idr"))
|
|
|
|
+ return id;
|
|
|
|
+
|
|
|
|
+ ctlr->bus_num = id;
|
|
}
|
|
}
|
|
|
|
|
|
INIT_LIST_HEAD(&ctlr->queue);
|
|
INIT_LIST_HEAD(&ctlr->queue);
|
|
@@ -2099,11 +2115,16 @@ int spi_register_controller(struct spi_controller *ctlr)
|
|
*/
|
|
*/
|
|
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
|
|
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
|
|
status = device_add(&ctlr->dev);
|
|
status = device_add(&ctlr->dev);
|
|
- if (status < 0)
|
|
|
|
|
|
+ if (status < 0) {
|
|
|
|
+ /* free bus id */
|
|
|
|
+ mutex_lock(&board_lock);
|
|
|
|
+ idr_remove(&spi_master_idr, ctlr->bus_num);
|
|
|
|
+ mutex_unlock(&board_lock);
|
|
goto done;
|
|
goto done;
|
|
- dev_dbg(dev, "registered %s %s%s\n",
|
|
|
|
|
|
+ }
|
|
|
|
+ dev_dbg(dev, "registered %s %s\n",
|
|
spi_controller_is_slave(ctlr) ? "slave" : "master",
|
|
spi_controller_is_slave(ctlr) ? "slave" : "master",
|
|
- dev_name(&ctlr->dev), dynamic ? " (dynamic)" : "");
|
|
|
|
|
|
+ dev_name(&ctlr->dev));
|
|
|
|
|
|
/* If we're using a queued driver, start the queue */
|
|
/* If we're using a queued driver, start the queue */
|
|
if (ctlr->transfer)
|
|
if (ctlr->transfer)
|
|
@@ -2112,6 +2133,10 @@ int spi_register_controller(struct spi_controller *ctlr)
|
|
status = spi_controller_initialize_queue(ctlr);
|
|
status = spi_controller_initialize_queue(ctlr);
|
|
if (status) {
|
|
if (status) {
|
|
device_del(&ctlr->dev);
|
|
device_del(&ctlr->dev);
|
|
|
|
+ /* free bus id */
|
|
|
|
+ mutex_lock(&board_lock);
|
|
|
|
+ idr_remove(&spi_master_idr, ctlr->bus_num);
|
|
|
|
+ mutex_unlock(&board_lock);
|
|
goto done;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2190,8 +2215,20 @@ static int __unregister(struct device *dev, void *null)
|
|
*/
|
|
*/
|
|
void spi_unregister_controller(struct spi_controller *ctlr)
|
|
void spi_unregister_controller(struct spi_controller *ctlr)
|
|
{
|
|
{
|
|
|
|
+ struct spi_controller *found;
|
|
int dummy;
|
|
int dummy;
|
|
|
|
|
|
|
|
+ /* First make sure that this controller was ever added */
|
|
|
|
+ mutex_lock(&board_lock);
|
|
|
|
+ found = idr_find(&spi_master_idr, ctlr->bus_num);
|
|
|
|
+ mutex_unlock(&board_lock);
|
|
|
|
+ if (found != ctlr) {
|
|
|
|
+ dev_dbg(&ctlr->dev,
|
|
|
|
+ "attempting to delete unregistered controller [%s]\n",
|
|
|
|
+ dev_name(&ctlr->dev));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (ctlr->queued) {
|
|
if (ctlr->queued) {
|
|
if (spi_destroy_queue(ctlr))
|
|
if (spi_destroy_queue(ctlr))
|
|
dev_err(&ctlr->dev, "queue remove failed\n");
|
|
dev_err(&ctlr->dev, "queue remove failed\n");
|
|
@@ -2203,6 +2240,10 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|
|
|
|
|
dummy = device_for_each_child(&ctlr->dev, NULL, __unregister);
|
|
dummy = device_for_each_child(&ctlr->dev, NULL, __unregister);
|
|
device_unregister(&ctlr->dev);
|
|
device_unregister(&ctlr->dev);
|
|
|
|
+ /* free bus id */
|
|
|
|
+ mutex_lock(&board_lock);
|
|
|
|
+ idr_remove(&spi_master_idr, ctlr->bus_num);
|
|
|
|
+ mutex_unlock(&board_lock);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(spi_unregister_controller);
|
|
EXPORT_SYMBOL_GPL(spi_unregister_controller);
|
|
|
|
|