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@xxxxxxxxxxxxxxxxxx> --- drivers/i2c/i2c-core-base.c | 2 +- drivers/i2c/i2c-core-of.c | 64 ++++++++++++++++++++++++++------------------- include/linux/i2c.h | 10 +++++++ 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index c89dac7fd2e7..ea1391d09266 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -762,7 +762,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; - client->dev.of_node = info->of_node; + client->dev.of_node = of_node_get(info->of_node); client->dev.fwnode = info->fwnode; i2c_dev_set_name(adap, client); diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index ccf82fdbcd8e..b201613e71e5 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -22,63 +22,73 @@ #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 *result; - struct i2c_board_info info = {}; - struct dev_archdata dev_ad = {}; - const __be32 *addr_be; u32 addr; - int len; + int ret; - dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name); + 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 %s\n", + if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) { + dev_err(dev, "of_i2c: modalias failure on %s\n", node->full_name); - return ERR_PTR(-EINVAL); + return -EINVAL; } - addr_be = of_get_property(node, "reg", &len); - if (!addr_be || (len < sizeof(*addr_be))) { - dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", + ret = of_property_read_u32(node, "reg", &addr); + if (ret) { + dev_err(dev, "of_i2c: invalid reg on %s\n", node->full_name); - return ERR_PTR(-EINVAL); + return ret; } - addr = be32_to_cpup(addr_be); 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; } - if (i2c_check_addr_validity(addr, info.flags)) { - dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", + ret = i2c_check_addr_validity(addr, info->flags); + if (ret) { + dev_err(dev, "of_i2c: invalid addr=%x on %s\n", addr, node->full_name); - return ERR_PTR(-EINVAL); + return ret; } - info.addr = addr; - info.of_node = of_node_get(node); - info.archdata = &dev_ad; + 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; +} + +static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, + struct device_node *node) +{ + struct i2c_client *result; + struct i2c_board_info info; + int ret; + + dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name); + + ret = of_i2c_get_board_info(&adap->dev, node, &info); + if (ret) + return ERR_PTR(ret); result = i2c_new_device(adap, &info); if (result == NULL) { dev_err(&adap->dev, "of_i2c: Failure registering %s\n", node->full_name); - of_node_put(node); return ERR_PTR(-EINVAL); } return result; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 00ca5b86a753..ea91270c258d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -806,6 +806,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) @@ -830,6 +833,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) -- 2.7.4