forked from mirrors/linux
		
	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: Feng Tang <feng.tang@intel.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
This commit is contained in:
		
							parent
							
								
									d4429f608a
								
							
						
					
					
						commit
						2b9603a0d7
					
				
					 2 changed files with 50 additions and 41 deletions
				
			
		|  | @ -29,11 +29,6 @@ | |||
| #include <linux/spi/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) | ||||
| { | ||||
| 	struct spi_device	*spi = to_spi_device(dev); | ||||
|  | @ -202,11 +197,16 @@ EXPORT_SYMBOL_GPL(spi_register_driver); | |||
| 
 | ||||
| struct boardinfo { | ||||
| 	struct list_head	list; | ||||
| 	unsigned		n_board_info; | ||||
| 	struct spi_board_info	board_info[0]; | ||||
| 	struct spi_board_info	board_info; | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -371,6 +371,20 @@ struct spi_device *spi_new_device(struct spi_master *master, | |||
| } | ||||
| 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 | ||||
|  * @info: array of chip descriptors | ||||
|  | @ -393,43 +407,25 @@ EXPORT_SYMBOL_GPL(spi_new_device); | |||
| int __init | ||||
| 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) | ||||
| 		return -ENOMEM; | ||||
| 	bi->n_board_info = n; | ||||
| 	memcpy(bi->board_info, info, n * sizeof *info); | ||||
| 
 | ||||
| 	mutex_lock(&board_lock); | ||||
| 	list_add_tail(&bi->list, &board_list); | ||||
| 	mutex_unlock(&board_lock); | ||||
| 	return 0; | ||||
| } | ||||
| 	for (i = 0; i < n; i++, bi++, info++) { | ||||
| 		struct spi_master *master; | ||||
| 
 | ||||
| /* 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); | ||||
| 		} | ||||
| 		memcpy(&bi->board_info, info, sizeof(*info)); | ||||
| 		mutex_lock(&board_lock); | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------------------------------*/ | ||||
|  | @ -512,6 +508,7 @@ int spi_register_master(struct spi_master *master) | |||
| { | ||||
| 	static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1); | ||||
| 	struct device		*dev = master->dev.parent; | ||||
| 	struct boardinfo	*bi; | ||||
| 	int			status = -ENODEV; | ||||
| 	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), | ||||
| 			dynamic ? " (dynamic)" : ""); | ||||
| 
 | ||||
| 	/* populate children from any spi device tables */ | ||||
| 	scan_boardinfo(master); | ||||
| 	mutex_lock(&board_lock); | ||||
| 	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; | ||||
| 
 | ||||
| 	/* Register devices from the device tree */ | ||||
|  | @ -579,7 +580,12 @@ void spi_unregister_master(struct spi_master *master) | |||
| { | ||||
| 	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); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(spi_unregister_master); | ||||
|  |  | |||
|  | @ -204,6 +204,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) | |||
| /**
 | ||||
|  * struct spi_master - interface to SPI master controller | ||||
|  * @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 | ||||
|  *	given SPI controller. | ||||
|  * @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 device	dev; | ||||
| 
 | ||||
| 	struct list_head list; | ||||
| 
 | ||||
| 	/* other than negative (== assign one dynamically), bus_num is fully
 | ||||
| 	 * board-specific.  usually that simplifies to being SOC-specific. | ||||
| 	 * example:  one SOC has three SPI controllers, numbered 0..2, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Feng Tang
						Feng Tang