Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+47 −41
Original line number Original line Diff line number Diff line
@@ -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);


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,
}
}
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
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
	return 0;
 * 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);
}
}


/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
@@ -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)
	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)
{
{
	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);
+3 −0
Original line number Original line Diff line number Diff line
@@ -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)
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,