diff options
author | Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> | 2015-07-27 17:30:50 +0300 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2015-08-10 08:37:36 +0200 |
commit | 48e9743dd6483c5fd3f10c8e42c60d52d64b0e27 (patch) | |
tree | d657778ce50ddf4a3f088842c0d18d1b29fdd5b3 | |
parent | 611e12ea0f121a31d9e9c4ce2a18a77abc2f28d6 (diff) |
i2c: core: add and export of_get_i2c_adapter_by_node() interface
of_find_i2c_adapter_by_node() call requires quite often missing
put_device(), and i2c_put_adapter() releases a device locked by
i2c_get_adapter() only. In general module_put(adapter->owner) and
put_device(dev) are not interchangeable.
This is a common error reproduction scenario as a result of the
misusage described above (for clearness this is run on iMX6 platform
with HDMI and I2C bus drivers compiled as kernel modules):
root@mx6q:~# lsmod | grep i2c
i2c_imx 10213 0
root@mx6q:~# lsmod | grep dw_hdmi_imx
dw_hdmi_imx 3631 0
dw_hdmi 11846 1 dw_hdmi_imx
imxdrm 8674 3 dw_hdmi_imx,imx_ipuv3_crtc,imx_ldb
drm_kms_helper 113765 5 dw_hdmi,imxdrm,imx_ipuv3_crtc,imx_ldb
root@mx6q:~# rmmod dw_hdmi_imx
root@mx6q:~# lsmod | grep i2c
i2c_imx 10213 -1
^^^^^
root@mx6q:~# rmmod i2c_imx
rmmod: ERROR: Module i2c_imx is in use
To fix existing users of these interfaces and to avoid any further
confusion and misusage in future, add one more interface
of_get_i2c_adapter_by_node(), it is similar to i2c_get_adapter() in
sense that an I2C bus device driver found and locked by user can be
correctly unlocked by i2c_put_adapter().
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r-- | drivers/i2c/i2c-core.c | 18 | ||||
-rw-r--r-- | include/linux/i2c.h | 7 |
2 files changed, 25 insertions, 0 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index f80992d0a608..07a83f34ed58 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1371,6 +1371,24 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) return adapter; } EXPORT_SYMBOL(of_find_i2c_adapter_by_node); + +/* must call i2c_put_adapter() when done with returned i2c_adapter device */ +struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) +{ + struct i2c_adapter *adapter; + + adapter = of_find_i2c_adapter_by_node(node); + if (!adapter) + return NULL; + + if (!try_module_get(adapter->owner)) { + put_device(&adapter->dev); + adapter = NULL; + } + + return adapter; +} +EXPORT_SYMBOL(of_get_i2c_adapter_by_node); #else static void of_i2c_register_devices(struct i2c_adapter *adap) { } #endif /* CONFIG_OF */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e83a738a3b87..e2c859b74f8b 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -638,6 +638,8 @@ extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); /* must call put_device() when done with returned i2c_adapter device */ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node); +/* must call i2c_put_adapter() when done with returned i2c_adapter device */ +struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node); #else static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) @@ -649,6 +651,11 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node { return NULL; } + +static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) +{ + return NULL; +} #endif /* CONFIG_OF */ #endif /* _LINUX_I2C_H */ |