forked from mirrors/linux
		
	spi/of: Add OF notifier handler
Add OF notifier handler needed for creating/destroying spi devices according to dynamic runtime changes in the DT live tree. This code is enabled when CONFIG_OF_DYNAMIC is selected. Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> Signed-off-by: Grant Likely <grant.likely@linaro.org> Reviewed-by: Mark Brown <broonie@kernel.org> Cc: <linux-spi@vger.kernel.org>
This commit is contained in:
		
							parent
							
								
									aff5e3f89a
								
							
						
					
					
						commit
						ce79d54ae4
					
				
					 1 changed files with 84 additions and 0 deletions
				
			
		|  | @ -2317,6 +2317,86 @@ EXPORT_SYMBOL_GPL(spi_write_then_read); | |||
| 
 | ||||
| /*-------------------------------------------------------------------------*/ | ||||
| 
 | ||||
| #if IS_ENABLED(CONFIG_OF_DYNAMIC) | ||||
| static int __spi_of_device_match(struct device *dev, void *data) | ||||
| { | ||||
| 	return dev->of_node == data; | ||||
| } | ||||
| 
 | ||||
| /* must call put_device() when done with returned spi_device device */ | ||||
| static struct spi_device *of_find_spi_device_by_node(struct device_node *node) | ||||
| { | ||||
| 	struct device *dev = bus_find_device(&spi_bus_type, NULL, node, | ||||
| 						__spi_of_device_match); | ||||
| 	return dev ? to_spi_device(dev) : NULL; | ||||
| } | ||||
| 
 | ||||
| static int __spi_of_master_match(struct device *dev, const void *data) | ||||
| { | ||||
| 	return dev->of_node == data; | ||||
| } | ||||
| 
 | ||||
| /* the spi masters are not using spi_bus, so we find it with another way */ | ||||
| static struct spi_master *of_find_spi_master_by_node(struct device_node *node) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 
 | ||||
| 	dev = class_find_device(&spi_master_class, NULL, node, | ||||
| 				__spi_of_master_match); | ||||
| 	if (!dev) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	/* reference got in class_find_device */ | ||||
| 	return container_of(dev, struct spi_master, dev); | ||||
| } | ||||
| 
 | ||||
| static int of_spi_notify(struct notifier_block *nb, unsigned long action, | ||||
| 			 void *arg) | ||||
| { | ||||
| 	struct of_reconfig_data *rd = arg; | ||||
| 	struct spi_master *master; | ||||
| 	struct spi_device *spi; | ||||
| 
 | ||||
| 	switch (of_reconfig_get_state_change(action, arg)) { | ||||
| 	case OF_RECONFIG_CHANGE_ADD: | ||||
| 		master = of_find_spi_master_by_node(rd->dn->parent); | ||||
| 		if (master == NULL) | ||||
| 			return NOTIFY_OK;	/* not for us */ | ||||
| 
 | ||||
| 		spi = of_register_spi_device(master, rd->dn); | ||||
| 		put_device(&master->dev); | ||||
| 
 | ||||
| 		if (IS_ERR(spi)) { | ||||
| 			pr_err("%s: failed to create for '%s'\n", | ||||
| 					__func__, rd->dn->full_name); | ||||
| 			return notifier_from_errno(PTR_ERR(spi)); | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case OF_RECONFIG_CHANGE_REMOVE: | ||||
| 		/* find our device by node */ | ||||
| 		spi = of_find_spi_device_by_node(rd->dn); | ||||
| 		if (spi == NULL) | ||||
| 			return NOTIFY_OK;	/* no? not meant for us */ | ||||
| 
 | ||||
| 		/* unregister takes one ref away */ | ||||
| 		spi_unregister_device(spi); | ||||
| 
 | ||||
| 		/* and put the reference of the find */ | ||||
| 		put_device(&spi->dev); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return NOTIFY_OK; | ||||
| } | ||||
| 
 | ||||
| static struct notifier_block spi_of_notifier = { | ||||
| 	.notifier_call = of_spi_notify, | ||||
| }; | ||||
| #else /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ | ||||
| extern struct notifier_block spi_of_notifier; | ||||
| #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ | ||||
| 
 | ||||
| static int __init spi_init(void) | ||||
| { | ||||
| 	int	status; | ||||
|  | @ -2334,6 +2414,10 @@ static int __init spi_init(void) | |||
| 	status = class_register(&spi_master_class); | ||||
| 	if (status < 0) | ||||
| 		goto err2; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_OF)) | ||||
| 		WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err2: | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Pantelis Antoniou
						Pantelis Antoniou