mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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