i2c/of: Add OF_RECONFIG notifier handler
CONFIG_OF_DYNAMIC enables runtime changes to the device tree which in turn may trigger addition or removal of devices from Linux. Add an OF_RECONFIG notifier handler to receive tree change events and to creating or destroy i2c devices as required. Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> [grant.likely: clean up #ifdefs and drop unneeded error handling] Signed-off-by: Grant Likely <grant.likely@linaro.org> Reviewed-by: Wolfram Sang <wsa@the-dreams.de> Cc: Rob Herring <robh+dt@kernel.org> Cc: linux-i2c@vger.kernel.org
This commit is contained in:
parent
a430a3455f
commit
ea7513bbc0
1 changed files with 52 additions and 0 deletions
|
@ -1951,6 +1951,52 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
|
|||
}
|
||||
EXPORT_SYMBOL(i2c_clients_command);
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
|
||||
static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *arg)
|
||||
{
|
||||
struct of_reconfig_data *rd = arg;
|
||||
struct i2c_adapter *adap;
|
||||
struct i2c_client *client;
|
||||
|
||||
switch (of_reconfig_get_state_change(action, rd)) {
|
||||
case OF_RECONFIG_CHANGE_ADD:
|
||||
adap = of_find_i2c_adapter_by_node(rd->dn->parent);
|
||||
if (adap == NULL)
|
||||
return NOTIFY_OK; /* not for us */
|
||||
|
||||
client = of_i2c_register_device(adap, rd->dn);
|
||||
put_device(&adap->dev);
|
||||
|
||||
if (IS_ERR(client)) {
|
||||
pr_err("%s: failed to create for '%s'\n",
|
||||
__func__, rd->dn->full_name);
|
||||
return notifier_from_errno(PTR_ERR(client));
|
||||
}
|
||||
break;
|
||||
case OF_RECONFIG_CHANGE_REMOVE:
|
||||
/* find our device by node */
|
||||
client = of_find_i2c_device_by_node(rd->dn);
|
||||
if (client == NULL)
|
||||
return NOTIFY_OK; /* no? not meant for us */
|
||||
|
||||
/* unregister takes one ref away */
|
||||
i2c_unregister_device(client);
|
||||
|
||||
/* and put the reference of the find */
|
||||
put_device(&client->dev);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
static struct notifier_block i2c_of_notifier = {
|
||||
.notifier_call = of_i2c_notify,
|
||||
};
|
||||
#else
|
||||
extern struct notifier_block i2c_of_notifier;
|
||||
#endif /* CONFIG_OF_DYNAMIC */
|
||||
|
||||
static int __init i2c_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
@ -1968,6 +2014,10 @@ static int __init i2c_init(void)
|
|||
retval = i2c_add_driver(&dummy_driver);
|
||||
if (retval)
|
||||
goto class_err;
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
|
||||
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
|
||||
|
||||
return 0;
|
||||
|
||||
class_err:
|
||||
|
@ -1981,6 +2031,8 @@ static int __init i2c_init(void)
|
|||
|
||||
static void __exit i2c_exit(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
|
||||
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
|
||||
i2c_del_driver(&dummy_driver);
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
class_compat_unregister(i2c_adapter_compat_class);
|
||||
|
|
Loading…
Reference in a new issue