On Thu, Apr 10, 2014 at 09:15:16PM -0700, Srinivas Pandruvada wrote: > ACPI specification allows multiple i2c addresses defined under one > ACPI device object. These addresses are defined using _CRS method. > The current implementation will pickup the last entry in _CRS, and > create an i2C device, ignoring all other previous entries for addresses. > > The ACPI specification does not define, whether these addresses for one > i2c device or for multiple i2c devices, which are defined under the same > ACPI device object. We need some appoach where i2c clients can enumerate > on each of the i2C address and/or have access to those extra addresses. > > This change addresses both cases: > - Create a new i2c device for each i2c address > - Allow each i2 client to get addresses of all other devices under > the same ACPI device object (companions or siblings) > > Example 1: > Here in the following example, there are two i2C address in _CRS. > They belong to two different physical chipsets, with two different i2c > address but part of a module. > Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings > { > Name (RBUF, ResourceTemplate () > { > I2cSerialBus (0x0068, ControllerInitiated, 0x00061A80, > AddressingMode7Bit, "\\_SB.I2C5", > 0x00, ResourceConsumer, , > ) > I2cSerialBus (0x000C, ControllerInitiated, 0x00061A80, > AddressingMode7Bit, "\\_SB.I2C5", > 0x00, ResourceConsumer, , > ) > Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, ) > { > 0x00000044, > } > }) > Return (RBUF) > } > This change adds i2c_new_device for each i2c address. Here contents of > /sys/bus/i2c/devices will > i2c-some_acpi_dev_name:00:068 > i2c-some_acpi_dev_name::00:00c > > Example 2 > > Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings > { > Name (SBUF, ResourceTemplate () > { > GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, > "\\_SB.GPO1", 0x00, ResourceConsumer, , > ) > { // Pin list > 0x0018 > } > I2cSerialBus (0x0010, ControllerInitiated, 0x00061A80, > AddressingMode7Bit, "\\_SB.I2C4", > 0x00, ResourceConsumer, , > ) > I2cSerialBus (0x000C, ControllerInitiated, 0x00061A80, > AddressingMode7Bit, "\\_SB.I2C4", > 0x00, ResourceConsumer, , > ) > I2cSerialBus (0x0054, ControllerInitiated, 0x00061A80, > AddressingMode7Bit, "\\_SB.I2C4", > 0x00, ResourceConsumer, , > ) > }) > Return (SBUF) /* \_SB_.I2C4.CAM1._CRS.SBUF */ > } > } > Here there are three i2c addresses for this device. How does the client driver see this? If I have a device that has multiple addresses but it is the same physical device, how does the driver deal with it? For example how drivers/misc/eeprom/at24.c could take advantage of this? I also think that this needs to documented somewhere under Documentation/i2c/* or so. > When there is only I2cSerialBus address is present then there is no > change. The device name will not include address. In this way if > some device is using hardcoded path, this change will not impact them. > > Here any i2c client driver simply need to add ACPI bindings. They will > be probed multiple times, the client will bind to correct i2c device, > based on checking its identity and returning error for other. > At the same time, any i2c client can call i2c_for_each_acpi_comp_client > to get the i2c client instances of companion addresses defined > under the same ACPI device. > > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx> > --- > drivers/i2c/i2c-core.c | 111 ++++++++++++++++++++++++++++++++++++++++++------- > include/linux/i2c.h | 13 ++++++ > 2 files changed, 109 insertions(+), 15 deletions(-) > > diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c > index 5fb80b8..a69a7b4 100644 > --- a/drivers/i2c/i2c-core.c > +++ b/drivers/i2c/i2c-core.c > @@ -627,7 +627,7 @@ static void i2c_dev_set_name(struct i2c_adapter *adap, > struct acpi_device *adev = ACPI_COMPANION(&client->dev); > > if (adev) { > - dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev)); > + dev_set_name(&client->dev, "i2c-%s", client->name); > return; > } > > @@ -1080,24 +1080,44 @@ static void of_i2c_register_devices(struct i2c_adapter *adap) { } > /* ACPI support code */ > > #if IS_ENABLED(CONFIG_ACPI) > +/* > + * acpi_i2c_add_resource is a callback, which is called while walking > + * name spaces, adding a limit allows for faster processing, without > + * using reallocation, > + * Adding some limit for max adresses in resource. Currently we see > + * max only 3 addresses, so 20 is more than enough > + */ > +#define MAX_CRS_ELEMENTS 20 > +struct i2c_resource_info { > + unsigned short addr[MAX_CRS_ELEMENTS]; > + unsigned short flags[MAX_CRS_ELEMENTS]; > + int count; > + int common_irq; > +}; > + > static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) > { > - struct i2c_board_info *info = data; > + struct i2c_resource_info *rcs_info = data; > > if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { > struct acpi_resource_i2c_serialbus *sb; > > sb = &ares->data.i2c_serial_bus; > if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { > - info->addr = sb->slave_address; > + if (rcs_info->count >= MAX_CRS_ELEMENTS) > + return 1; /* Simply ignore adding */ > + rcs_info->addr[rcs_info->count] = > + sb->slave_address; > if (sb->access_mode == ACPI_I2C_10BIT_MODE) > - info->flags |= I2C_CLIENT_TEN; > + rcs_info->flags[rcs_info->count] = > + I2C_CLIENT_TEN; > + rcs_info->count++; > } > - } else if (info->irq < 0) { > + } else if (rcs_info->common_irq < 0) { > struct resource r; > > if (acpi_dev_resource_interrupt(ares, 0, &r)) > - info->irq = r.start; > + rcs_info->common_irq = r.start; > } > > /* Tell the ACPI core to skip this resource */ > @@ -1111,7 +1131,10 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, > struct list_head resource_list; > struct i2c_board_info info; > struct acpi_device *adev; > + struct i2c_resource_info rcs_info; > + struct i2c_client *i2c_client; > int ret; > + int i; > > if (acpi_bus_get_device(handle, &adev)) > return AE_OK; > @@ -1120,23 +1143,42 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, > > memset(&info, 0, sizeof(info)); > info.acpi_node.companion = adev; > - info.irq = -1; > + > + memset(&rcs_info, 0, sizeof(rcs_info)); > + rcs_info.common_irq = -1; > > INIT_LIST_HEAD(&resource_list); > ret = acpi_dev_get_resources(adev, &resource_list, > - acpi_i2c_add_resource, &info); > + acpi_i2c_add_resource, &rcs_info); > acpi_dev_free_resource_list(&resource_list); > > - if (ret < 0 || !info.addr) > + if (ret < 0) > return AE_OK; > > adev->power.flags.ignore_parent = true; > - strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); > - if (!i2c_new_device(adapter, &info)) { > - adev->power.flags.ignore_parent = false; > - dev_err(&adapter->dev, > - "failed to add I2C device %s from ACPI\n", > - dev_name(&adev->dev)); > + info.irq = rcs_info.common_irq; > + for (i = 0; i < rcs_info.count; ++i) { Instead of this, would it make sense if you do the device creation in acpi_i2c_add_resource() for each enumerated device? At least it would look better than this ;-) > + if (!rcs_info.addr[i]) > + continue; > + info.addr = rcs_info.addr[i]; > + info.flags = rcs_info.flags[i]; > + /* Status quo, when only one address is present */ > + if (rcs_info.count > 1) > + snprintf(info.type, sizeof(info.type), "%s:%03x", > + dev_name(&adev->dev), > + info.addr); > + else > + strlcpy(info.type, dev_name(&adev->dev), > + sizeof(info.type)); > + i2c_client = i2c_new_device(adapter, &info); > + if (!i2c_client) { > + if (!i) > + adev->power.flags.ignore_parent = false; > + dev_err(&adapter->dev, > + "failed to add I2C device %s from ACPI\n", > + dev_name(&adev->dev)); > + break; > + } > } > > return AE_OK; > @@ -1168,6 +1210,45 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap) > if (ACPI_FAILURE(status)) > dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); > } > + > +/* Helper functions to get companion addresses */ > +struct acpi_comp_arg { > + struct acpi_device *acpi_dev; > + void (*callback)(struct i2c_client *); > +}; > + > +static int i2c_acpi_companion(struct device *dev, void *_arg) > +{ > + struct acpi_comp_arg *arg = _arg; > + > + if (arg->acpi_dev == ACPI_COMPANION(dev)) { > + struct i2c_client *client; > + > + client = to_i2c_client(dev); > + arg->callback(client); > + } > + > + return 0; > +} > + Missing kernel doc here. > +int i2c_for_each_acpi_comp_client(struct i2c_client *client, > + void (*callback)(struct i2c_client *)) > +{ > + struct acpi_comp_arg arg; > + int found; > + > + if (!client) > + return -EINVAL; > + > + arg.acpi_dev = ACPI_COMPANION(&client->dev); > + arg.callback = callback; > + found = device_for_each_child(&client->adapter->dev, &arg, > + i2c_acpi_companion); > + > + return found; > +} > +EXPORT_SYMBOL_GPL(i2c_for_each_acpi_comp_client); Since devices with multiple addresses seem to exist in real world, I think it would be beneficial to add a bit more generic implementation of the above to the I2C core instead. Then non-ACPI systems could also take advantage of it. > + > #else > static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {} > #endif /* CONFIG_ACPI */ > diff --git a/include/linux/i2c.h b/include/linux/i2c.h > index deddeb8..ce75c73 100644 > --- a/include/linux/i2c.h > +++ b/include/linux/i2c.h > @@ -323,6 +323,19 @@ extern struct i2c_client * > i2c_new_dummy(struct i2c_adapter *adap, u16 address); > > extern void i2c_unregister_device(struct i2c_client *); > + > +#if IS_ENABLED(CONFIG_ACPI) > +/* Callback to get i2c_client instance for ACPI i2 resource */ > +int i2c_for_each_acpi_comp_client(struct i2c_client *client, > + void (*callback)(struct i2c_client *)); > +#else > +int i2c_for_each_acpi_comp_client(struct i2c_client *client, > + int (*callback)(struct i2c_client *)) > +{ > + return 0; > +} > +#endif > + > #endif /* I2C */ > > /* Mainboard arch_initcall() code should register all its I2C devices. > -- > 1.7.11.7 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html