diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 9fcfd5c..c607bcb 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -545,6 +545,7 @@ i2c_fsl->adapter.master_xfer = i2c_fsl_xfer; i2c_fsl->adapter.nr = pdev->id; i2c_fsl->adapter.dev.parent = pdev; + i2c_fsl->adapter.dev.device_node = pdev->device_node; i2c_fsl->base = dev_request_mem_region(pdev, 0); i2c_fsl->dfsrr = -1; @@ -572,8 +573,17 @@ return ret; } +static __maybe_unused struct of_device_id imx_i2c_dt_ids[] = { + { + .compatible = "fsl,imx21-i2c", + }, { + /* sentinel */ + } +}; + static struct driver_d i2c_fsl_driver = { .probe = i2c_fsl_probe, .name = DRIVER_NAME, + .of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids), }; device_platform_driver(i2c_fsl_driver); diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 4862df3..b63d946 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -254,33 +254,61 @@ client->dev.platform_data = chip->platform_data; client->dev.id = DEVICE_ID_DYNAMIC; client->dev.bus = &i2c_bus; + client->dev.device_node = chip->of_node; client->adapter = adapter; client->addr = chip->addr; client->dev.parent = &adapter->dev; status = register_device(&client->dev); - -#if 0 - /* drivers may modify this initial i/o setup */ - status = master->setup(client); - if (status < 0) { - printf("can't setup %s, status %d\n", - client->dev.name, status); - goto fail; + if (status) { + free(client); + return NULL; } -#endif return client; - -#if 0 - fail: - free(proxy); - return NULL; -#endif } EXPORT_SYMBOL(i2c_new_device); +void of_i2c_register_devices(struct i2c_adapter *adap) +{ + struct device_node *n; + + /* Only register child devices if the adapter has a node pointer set */ + if (!IS_ENABLED(CONFIG_OFDEVICE) || !adap->dev.device_node) + return; + + device_node_for_nach_child(adap->dev.device_node, n) { + struct i2c_board_info info = {}; + struct i2c_client *result; + const __be32 *addr; + int len; + + of_modalias_node(n, info.type, I2C_NAME_SIZE); + + info.of_node = n; + + addr = of_get_property(n, "reg", &len); + if (!addr || (len < sizeof(int))) { + dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", + n->full_name); + continue; + } + + info.addr = be32_to_cpup(addr); + if (info.addr > (1 << 10) - 1) { + dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", + info.addr, n->full_name); + continue; + } + + result = i2c_new_device(adap, &info); + if (!result) + dev_err(&adap->dev, "of_i2c: Failure registering %s\n", + n->full_name); + } +} + /** * i2c_new_dummy - return a new i2c device bound to a dummy driver * @adapter: the adapter managing the device @@ -396,8 +424,17 @@ { int ret; - if (i2c_get_adapter(adapter->nr)) - return -EBUSY; + if (adapter->nr < 0) { + int nr; + + for (nr = 0;; nr++) + if (!i2c_get_adapter(nr)) + break; + adapter->nr = nr; + } else { + if (i2c_get_adapter(adapter->nr)) + return -EBUSY; + } adapter->dev.id = adapter->nr; strcpy(adapter->dev.name, "i2c"); @@ -411,6 +448,8 @@ /* populate children from any i2c device tables */ scan_boardinfo(adapter); + of_i2c_register_devices(adapter); + return 0; } EXPORT_SYMBOL(i2c_add_numbered_adapter); diff --git a/drivers/of/base.c b/drivers/of/base.c index e0d7fd0..4241e65 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -656,6 +656,33 @@ } EXPORT_SYMBOL_GPL(of_property_read_string_index); +/** + * of_modalias_node - Lookup appropriate modalias for a device node + * @node: pointer to a device tree node + * @modalias: Pointer to buffer that modalias value will be copied into + * @len: Length of modalias value + * + * Based on the value of the compatible property, this routine will attempt + * to choose an appropriate modalias value for a particular device tree node. + * It does this by stripping the manufacturer prefix (as delimited by a ',') + * from the first entry in the compatible list property. + * + * This routine returns 0 on success, <0 on failure. + */ +int of_modalias_node(struct device_node *node, char *modalias, int len) +{ + const char *compatible, *p; + int cplen; + + compatible = of_get_property(node, "compatible", &cplen); + if (!compatible || strlen(compatible) > cplen) + return -ENODEV; + p = strchr(compatible, ','); + strlcpy(modalias, p ? p + 1 : compatible, len); + return 0; +} +EXPORT_SYMBOL_GPL(of_modalias_node); + struct device_node *of_get_root_node(void) { return root_node; diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index dab8dc5..46185ac 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -105,6 +105,7 @@ char type[I2C_NAME_SIZE]; /**< name of device */ unsigned short addr; /**< stored in i2c_client.addr */ void *platform_data; /**< platform data for device */ + struct device_node *of_node; }; /** diff --git a/include/of.h b/include/of.h index f8a45a7..300b706 100644 --- a/include/of.h +++ b/include/of.h @@ -72,6 +72,8 @@ struct fdt_header *of_get_fixed_tree(struct device_node *node); +int of_modalias_node(struct device_node *node, char *modalias, int len); + #define device_node_for_nach_child(node, child) \ list_for_each_entry(child, &node->children, parent_list)