2017-08-03 04:36:10

by Suniel Mahesh

[permalink] [raw]
Subject: [PATCH] drivers: spi: Pick spi bus number from Linux idr or spi alias

From: Suniel Mahesh <[email protected]>

Modify existing code, for automatically picking the spi bus number based
on Linux idr scheme as mentioned in FIXME.
This patch does the following:
(a) Remove the now unnecessary code which was allocating bus numbers using
ATOMIC_INIT and atomic_dec_return macro's.
(b) If we have an alias, pick the bus number from alias ID
(c) Convert to linux idr interface

Signed-off-by: Suniel Mahesh <[email protected]>
Signed-off-by: Karthik Tummala <[email protected]>
Tested-by: Karthik Tummala <[email protected]>
---
Note:
- Patch was compile tested and built(ARCH=arm) on next-20170802.
- Patch was hardware tested on AM335x (McSPI controller) with
memory chips as slaves.
- Added spi aliases in aliases node, device tree and tested.
- No build/run-time issues reported.
---
drivers/spi/spi.c | 71 +++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 56 insertions(+), 15 deletions(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ec69293..17bec04d 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -40,9 +40,13 @@
#include <linux/ioport.h>
#include <linux/acpi.h>
#include <linux/highmem.h>
+#include <linux/idr.h>

#define CREATE_TRACE_POINTS
#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)
{
@@ -420,6 +424,7 @@ struct boardinfo {
/*
* Used to protect add/del opertion for board_info list and
* spi_controller list, and their matching process
+ * also used to protect object of type struct idr
*/
static DEFINE_MUTEX(board_lock);

@@ -2046,11 +2051,10 @@ static int of_spi_register_master(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 boardinfo *bi;
int status = -ENODEV;
- int dynamic = 0;
+ int id;

if (!dev)
return -ENODEV;
@@ -2066,17 +2070,29 @@ int spi_register_controller(struct spi_controller *ctlr)
*/
if (ctlr->num_chipselect == 0)
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) {
- /* 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);
@@ -2094,11 +2110,16 @@ int spi_register_controller(struct spi_controller *ctlr)
*/
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
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;
- dev_dbg(dev, "registered %s %s%s\n",
+ }
+ dev_dbg(dev, "registered %s %s\n",
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 (ctlr->transfer)
@@ -2107,6 +2128,10 @@ int spi_register_controller(struct spi_controller *ctlr)
status = spi_controller_initialize_queue(ctlr);
if (status) {
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;
}
}
@@ -2185,8 +2210,20 @@ static int __unregister(struct device *dev, void *null)
*/
void spi_unregister_controller(struct spi_controller *ctlr)
{
+ struct spi_controller *found;
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 (spi_destroy_queue(ctlr))
dev_err(&ctlr->dev, "queue remove failed\n");
@@ -2198,6 +2235,10 @@ void spi_unregister_controller(struct spi_controller *ctlr)

dummy = device_for_each_child(&ctlr->dev, NULL, __unregister);
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);

--
1.9.1