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
|
@ -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 New Issue
Block a user