[v4,3/3] i2c: Export of_i2c_get_board_info()

Message ID 20180325124903.2909-4-boris.brezillon@bootlin.com
State Accepted
Headers show
Series
  • i2c: Prepare things for I3C
Related show

Commit Message

Boris Brezillon March 25, 2018, 12:49 p.m.
I3C busses have to know about all I2C devices connected on the I3C bus
to properly initialize the I3C master, and I2C frames can't be sent on
the bus until this initialization is done.

We can't let the I2C core parse the DT and instantiate I2C devices as
part of its i2c_add_adapter() procedure because, when done this way,
I2C devices are directly registered to the device-model and might be
attached to drivers which could in turn start sending frames on the bus,
which won't work since, as said above, the bus is not yet initialized.

Export of_i2c_register_device() in order to let the I3C core parse the
I2C device nodes by itself and initialize the bus.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v4:
- document of_i2c_get_board_info()
- rebased on i2c/for-next
- moved the ->archdata assignment removal in a separate patch
- moved the of_node_get/put() changes in a separate patch

Changes in v3:
- rebased on v4.16-rc1

Changes in v2:
- fix memset() call
- rebase on v4.15-rc1
---
 drivers/i2c/i2c-core-of.c | 48 ++++++++++++++++++++++++++++++-----------------
 include/linux/i2c.h       | 10 ++++++++++
 2 files changed, 41 insertions(+), 17 deletions(-)

Comments

Wolfram Sang May 22, 2018, 11:43 a.m. | #1
On Sun, Mar 25, 2018 at 02:49:03PM +0200, Boris Brezillon wrote:
> I3C busses have to know about all I2C devices connected on the I3C bus
> to properly initialize the I3C master, and I2C frames can't be sent on
> the bus until this initialization is done.
> 
> We can't let the I2C core parse the DT and instantiate I2C devices as
> part of its i2c_add_adapter() procedure because, when done this way,
> I2C devices are directly registered to the device-model and might be
> attached to drivers which could in turn start sending frames on the bus,
> which won't work since, as said above, the bus is not yet initialized.
> 
> Export of_i2c_register_device() in order to let the I3C core parse the
> I2C device nodes by itself and initialize the bus.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>

Applied to for-next, thanks!

Patch

diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c
index 9fb38e99a6c6..6cb7ad608bcd 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -22,46 +22,60 @@ 
 
 #include "i2c-core.h"
 
-static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
-						 struct device_node *node)
+int of_i2c_get_board_info(struct device *dev, struct device_node *node,
+			  struct i2c_board_info *info)
 {
-	struct i2c_client *client;
-	struct i2c_board_info info = {};
 	u32 addr;
 	int ret;
 
-	dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
+	memset(info, 0, sizeof(*info));
 
-	if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
-		dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n",
-			node);
-		return ERR_PTR(-EINVAL);
+	if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
+		dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
+		return -EINVAL;
 	}
 
 	ret = of_property_read_u32(node, "reg", &addr);
 	if (ret) {
-		dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node);
-		return ERR_PTR(ret);
+		dev_err(dev, "of_i2c: invalid reg on %pOF\n", node);
+		return ret;
 	}
 
 	if (addr & I2C_TEN_BIT_ADDRESS) {
 		addr &= ~I2C_TEN_BIT_ADDRESS;
-		info.flags |= I2C_CLIENT_TEN;
+		info->flags |= I2C_CLIENT_TEN;
 	}
 
 	if (addr & I2C_OWN_SLAVE_ADDRESS) {
 		addr &= ~I2C_OWN_SLAVE_ADDRESS;
-		info.flags |= I2C_CLIENT_SLAVE;
+		info->flags |= I2C_CLIENT_SLAVE;
 	}
 
-	info.addr = addr;
-	info.of_node = node;
+	info->addr = addr;
+	info->of_node = node;
 
 	if (of_property_read_bool(node, "host-notify"))
-		info.flags |= I2C_CLIENT_HOST_NOTIFY;
+		info->flags |= I2C_CLIENT_HOST_NOTIFY;
 
 	if (of_get_property(node, "wakeup-source", NULL))
-		info.flags |= I2C_CLIENT_WAKE;
+		info->flags |= I2C_CLIENT_WAKE;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_i2c_get_board_info);
+
+static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
+						 struct device_node *node)
+{
+	struct i2c_client *client;
+	struct i2c_board_info info;
+	int ret;
+
+	dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
+
+	ret = of_i2c_get_board_info(&adap->dev, node, &info);
+	if (ret)
+		return ERR_PTR(ret);
 
 	client = i2c_new_device(adap, &info);
 	if (!client) {
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index aeb655772ef8..254cd34eeae2 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -901,6 +901,9 @@  extern const struct of_device_id
 *i2c_of_match_device(const struct of_device_id *matches,
 		     struct i2c_client *client);
 
+int of_i2c_get_board_info(struct device *dev, struct device_node *node,
+			  struct i2c_board_info *info);
+
 #else
 
 static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
@@ -925,6 +928,13 @@  static inline const struct of_device_id
 	return NULL;
 }
 
+static inline int of_i2c_get_board_info(struct device *dev,
+					struct device_node *node,
+					struct i2c_board_info *info)
+{
+	return -ENOTSUPP;
+}
+
 #endif /* CONFIG_OF */
 
 #if IS_ENABLED(CONFIG_ACPI)