On Tue, Jun 10, 2014 at 08:09:22AM -0700, Srinivas Pandruvada wrote:
> On 06/10/2014 02:24 AM, Mika Westerberg wrote:
> >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?
> >
> 
> Use i2c_for_each_acpi_comp_client. I use similar approach as
> i2c_for_each_dev already defined in i2c.h.

But now probe is called multiple times (with a different i2c_client
passed in so I need to keep some account about that, right? Doing that
makes the drivers more complex.

> 
> >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.pandruv...@linux.intel.com>
> >>---
> >>  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?
> >
> 
> You can't. Because the interrupt resource is common. You may see that
> definition (as above examples), you have to go through the list before, to
> find out irq number, which is needed to create i2c device, if there
> is any IRQ.

Indeed. So how about first looking for the IRQ and then add the devices?
Or something like that.

> 
> >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.
> >
> What is your suggestion? i2c already use similar method for finding each
> i2c_for_each_dev, I felt similar method would be sufficient.

Well, how about linking relative i2c_clients? So that you can walk the
list if you have multi-address device.

> 
> >>+
> >>  #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 majord...@vger.kernel.org
> >>More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >>Please read the FAQ at  http://www.tux.org/lkml/
> >
> Thanks,
> Srinivas
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to