Commit 2b9603a0 authored by Feng Tang's avatar Feng Tang Committed by Grant Likely

spi: enable spi_board_info to be registered after spi_master

Currently spi_register_board_info() has to be called before its related
spi_master be registered, otherwise these board info will be just ignored.

This patch will remove this order limit, it adds a global spi master list
like the existing global board info listr. Whenever a board info or a
spi_master is registered, the spi master list or board info list
will be scanned, and a new spi device will be created if there is a
master-board info match.
Signed-off-by: default avatarFeng Tang <feng.tang@intel.com>
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
parent d4429f60
...@@ -29,11 +29,6 @@ ...@@ -29,11 +29,6 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/of_spi.h> #include <linux/of_spi.h>
/* SPI bustype and spi_master class are registered after board init code
* provides the SPI device tables, ensuring that both are present by the
* time controller driver registration causes spi_devices to "enumerate".
*/
static void spidev_release(struct device *dev) static void spidev_release(struct device *dev)
{ {
struct spi_device *spi = to_spi_device(dev); struct spi_device *spi = to_spi_device(dev);
...@@ -202,11 +197,16 @@ EXPORT_SYMBOL_GPL(spi_register_driver); ...@@ -202,11 +197,16 @@ EXPORT_SYMBOL_GPL(spi_register_driver);
struct boardinfo { struct boardinfo {
struct list_head list; struct list_head list;
unsigned n_board_info; struct spi_board_info board_info;
struct spi_board_info board_info[0];
}; };
static LIST_HEAD(board_list); static LIST_HEAD(board_list);
static LIST_HEAD(spi_master_list);
/*
* Used to protect add/del opertion for board_info list and
* spi_master list, and their matching process
*/
static DEFINE_MUTEX(board_lock); static DEFINE_MUTEX(board_lock);
/** /**
...@@ -371,6 +371,20 @@ struct spi_device *spi_new_device(struct spi_master *master, ...@@ -371,6 +371,20 @@ struct spi_device *spi_new_device(struct spi_master *master,
} }
EXPORT_SYMBOL_GPL(spi_new_device); EXPORT_SYMBOL_GPL(spi_new_device);
static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi)
{
struct spi_device *dev;
if (master->bus_num != bi->bus_num)
return;
dev = spi_new_device(master, bi);
if (!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",
bi->modalias);
}
/** /**
* spi_register_board_info - register SPI devices for a given board * spi_register_board_info - register SPI devices for a given board
* @info: array of chip descriptors * @info: array of chip descriptors
...@@ -394,42 +408,24 @@ int __init ...@@ -394,42 +408,24 @@ int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n) spi_register_board_info(struct spi_board_info const *info, unsigned n)
{ {
struct boardinfo *bi; struct boardinfo *bi;
int i;
bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL); bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
if (!bi) if (!bi)
return -ENOMEM; return -ENOMEM;
bi->n_board_info = n;
memcpy(bi->board_info, info, n * sizeof *info);
for (i = 0; i < n; i++, bi++, info++) {
struct spi_master *master;
memcpy(&bi->board_info, info, sizeof(*info));
mutex_lock(&board_lock); mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list); list_add_tail(&bi->list, &board_list);
list_for_each_entry(master, &spi_master_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock); mutex_unlock(&board_lock);
return 0;
}
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
mutex_lock(&board_lock);
list_for_each_entry(bi, &board_list, list) {
struct spi_board_info *chip = bi->board_info;
unsigned n;
for (n = bi->n_board_info; n > 0; n--, chip++) {
if (chip->bus_num != master->bus_num)
continue;
/* NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
(void) spi_new_device(master, chip);
}
} }
mutex_unlock(&board_lock);
return 0;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -512,6 +508,7 @@ int spi_register_master(struct spi_master *master) ...@@ -512,6 +508,7 @@ int spi_register_master(struct spi_master *master)
{ {
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device *dev = master->dev.parent; struct device *dev = master->dev.parent;
struct boardinfo *bi;
int status = -ENODEV; int status = -ENODEV;
int dynamic = 0; int dynamic = 0;
...@@ -547,8 +544,12 @@ int spi_register_master(struct spi_master *master) ...@@ -547,8 +544,12 @@ int spi_register_master(struct spi_master *master)
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : ""); dynamic ? " (dynamic)" : "");
/* populate children from any spi device tables */ mutex_lock(&board_lock);
scan_boardinfo(master); list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock);
status = 0; status = 0;
/* Register devices from the device tree */ /* Register devices from the device tree */
...@@ -579,7 +580,12 @@ void spi_unregister_master(struct spi_master *master) ...@@ -579,7 +580,12 @@ void spi_unregister_master(struct spi_master *master)
{ {
int dummy; int dummy;
dummy = device_for_each_child(&master->dev, NULL, __unregister); mutex_lock(&board_lock);
list_del(&master->list);
mutex_unlock(&board_lock);
dummy = device_for_each_child(master->dev.parent, &master->dev,
__unregister);
device_unregister(&master->dev); device_unregister(&master->dev);
} }
EXPORT_SYMBOL_GPL(spi_unregister_master); EXPORT_SYMBOL_GPL(spi_unregister_master);
......
...@@ -204,6 +204,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) ...@@ -204,6 +204,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
/** /**
* struct spi_master - interface to SPI master controller * struct spi_master - interface to SPI master controller
* @dev: device interface to this driver * @dev: device interface to this driver
* @list: link with the global spi_master list
* @bus_num: board-specific (and often SOC-specific) identifier for a * @bus_num: board-specific (and often SOC-specific) identifier for a
* given SPI controller. * given SPI controller.
* @num_chipselect: chipselects are used to distinguish individual * @num_chipselect: chipselects are used to distinguish individual
...@@ -238,6 +239,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) ...@@ -238,6 +239,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
struct spi_master { struct spi_master {
struct device dev; struct device dev;
struct list_head list;
/* other than negative (== assign one dynamically), bus_num is fully /* other than negative (== assign one dynamically), bus_num is fully
* board-specific. usually that simplifies to being SOC-specific. * board-specific. usually that simplifies to being SOC-specific.
* example: one SOC has three SPI controllers, numbered 0..2, * example: one SOC has three SPI controllers, numbered 0..2,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment