From 166c2ba398640278ae6037be4aa5562c03cf3d24 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 7 Oct 2015 13:18:44 +0300 Subject: i2c / ACPI: Rework I2C device scanning The way we currently scan I2C devices behind an I2C host controller does not work in cases where the I2C device in question is not declared directly below the host controller ACPI node. This is perfectly legal according the ACPI 6.0 specification and some existing systems are doing this. To be able to enumerate all devices which are connected to a certain I2C host controller we need to rework the current I2C scanning routine a bit. Instead of scanning directly below the host controller we scan the whole ACPI namespace for present devices with valid I2cSerialBus() connection pointing to the host controller in question. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Andy Shevchenko Tested-by: Dustin Byford Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 82 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 23 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 5f89f1e3c2f2..3a4c54ef290d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -99,27 +99,40 @@ struct gsb_buffer { }; } __packed; -static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) +struct acpi_i2c_lookup { + struct i2c_board_info *info; + acpi_handle adapter_handle; + acpi_handle device_handle; +}; + +static int acpi_i2c_find_address(struct acpi_resource *ares, void *data) { - struct i2c_board_info *info = data; + struct acpi_i2c_lookup *lookup = data; + struct i2c_board_info *info = lookup->info; + struct acpi_resource_i2c_serialbus *sb; + acpi_handle adapter_handle; + acpi_status status; - if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { - struct acpi_resource_i2c_serialbus *sb; + if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; - sb = &ares->data.i2c_serial_bus; - if (!info->addr && sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { - info->addr = sb->slave_address; - if (sb->access_mode == ACPI_I2C_10BIT_MODE) - info->flags |= I2C_CLIENT_TEN; - } - } else if (!info->irq) { - struct resource r; + sb = &ares->data.i2c_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) + return 1; - if (acpi_dev_resource_interrupt(ares, 0, &r)) - info->irq = r.start; + /* + * Extract the ResourceSource and make sure that the handle matches + * with the I2C adapter handle. + */ + status = acpi_get_handle(lookup->device_handle, + sb->resource_source.string_ptr, + &adapter_handle); + if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) { + info->addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; } - /* Tell the ACPI core to skip this resource */ return 1; } @@ -128,6 +141,8 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, { struct i2c_adapter *adapter = data; struct list_head resource_list; + struct acpi_i2c_lookup lookup; + struct resource_entry *entry; struct i2c_board_info info; struct acpi_device *adev; int ret; @@ -140,14 +155,37 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, memset(&info, 0, sizeof(info)); info.fwnode = acpi_fwnode_handle(adev); + memset(&lookup, 0, sizeof(lookup)); + lookup.adapter_handle = ACPI_HANDLE(adapter->dev.parent); + lookup.device_handle = handle; + lookup.info = &info; + + /* + * Look up for I2cSerialBus resource with ResourceSource that + * matches with this adapter. + */ INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, - acpi_i2c_add_resource, &info); + acpi_i2c_find_address, &lookup); acpi_dev_free_resource_list(&resource_list); if (ret < 0 || !info.addr) return AE_OK; + /* Then fill IRQ number if any */ + ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (ret < 0) + return AE_OK; + + resource_list_for_each_entry(entry, &resource_list) { + if (resource_type(entry->res) == IORESOURCE_IRQ) { + info.irq = entry->res->start; + break; + } + } + + acpi_dev_free_resource_list(&resource_list); + adev->power.flags.ignore_parent = true; strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); if (!i2c_new_device(adapter, &info)) { @@ -160,6 +198,8 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, return AE_OK; } +#define ACPI_I2C_MAX_SCAN_DEPTH 32 + /** * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter * @adap: pointer to adapter @@ -170,17 +210,13 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, */ static void acpi_i2c_register_devices(struct i2c_adapter *adap) { - acpi_handle handle; acpi_status status; - if (!adap->dev.parent) - return; - - handle = ACPI_HANDLE(adap->dev.parent); - if (!handle) + if (!adap->dev.parent || !has_acpi_companion(adap->dev.parent)) return; - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_I2C_MAX_SCAN_DEPTH, acpi_i2c_add_device, NULL, adap, NULL); if (ACPI_FAILURE(status)) -- cgit v1.2.3 From 8eb5c87a92c065aaca39ac3e841b07906a4959a2 Mon Sep 17 00:00:00 2001 From: Dustin Byford Date: Fri, 23 Oct 2015 12:27:07 -0700 Subject: i2c: add ACPI support for I2C mux ports Although I2C mux devices are easily enumerated using ACPI (_HID/_CID or device property compatible string match), enumerating I2C client devices connected through an I2C mux needs a little extra work. This change implements a method for describing an I2C device hierarchy that includes mux devices by using an ACPI Device() for each mux channel along with an _ADR to set the channel number for the device. See Documentation/acpi/i2c-muxes.txt for a simple example. To make this work the ismt, i801, and designware pci/platform devs now share an ACPI companion with their I2C adapter dev similar to how it's done in OF. This is done on the assumption that power management functions will not be called directly on the I2C dev that is sharing the ACPI node. Acked-by: Mika Westerberg Tested-by: Mika Westerberg Signed-off-by: Dustin Byford Signed-off-by: Wolfram Sang --- Documentation/acpi/i2c-muxes.txt | 58 +++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-designware-pcidrv.c | 2 + drivers/i2c/busses/i2c-designware-platdrv.c | 1 + drivers/i2c/busses/i2c-i801.c | 9 ++--- drivers/i2c/busses/i2c-ismt.c | 8 +--- drivers/i2c/i2c-core.c | 4 +- drivers/i2c/i2c-mux.c | 8 ++++ 7 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 Documentation/acpi/i2c-muxes.txt (limited to 'drivers/i2c/i2c-core.c') diff --git a/Documentation/acpi/i2c-muxes.txt b/Documentation/acpi/i2c-muxes.txt new file mode 100644 index 000000000000..9fcc4f0b885e --- /dev/null +++ b/Documentation/acpi/i2c-muxes.txt @@ -0,0 +1,58 @@ +ACPI I2C Muxes +-------------- + +Describing an I2C device hierarchy that includes I2C muxes requires an ACPI +Device () scope per mux channel. + +Consider this topology: + ++------+ +------+ +| SMB1 |-->| MUX0 |--CH00--> i2c client A (0x50) +| | | 0x70 |--CH01--> i2c client B (0x50) ++------+ +------+ + +which corresponds to the following ASL: + +Device (SMB1) +{ + Name (_HID, ...) + Device (MUX0) + { + Name (_HID, ...) + Name (_CRS, ResourceTemplate () { + I2cSerialBus (0x70, ControllerInitiated, I2C_SPEED, + AddressingMode7Bit, "^SMB1", 0x00, + ResourceConsumer,,) + } + + Device (CH00) + { + Name (_ADR, 0) + + Device (CLIA) + { + Name (_HID, ...) + Name (_CRS, ResourceTemplate () { + I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, + AddressingMode7Bit, "^CH00", 0x00, + ResourceConsumer,,) + } + } + } + + Device (CH01) + { + Name (_ADR, 1) + + Device (CLIB) + { + Name (_HID, ...) + Name (_CRS, ResourceTemplate () { + I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, + AddressingMode7Bit, "^CH01", 0x00, + ResourceConsumer,,) + } + } + } + } +} diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 169be1e26241..1543d35d228d 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "i2c-designware-core.h" #define DRIVER_NAME "i2c-designware-pci" @@ -244,6 +245,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, adap = &dev->adapter; adap->owner = THIS_MODULE; adap->class = 0; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->nr = controller->bus_num; r = i2c_dw_probe(dev); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index f830da6bf25a..190a0d2b94e7 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -207,6 +207,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) adap = &dev->adapter; adap->owner = THIS_MODULE; adap->class = I2C_CLASS_DEPRECATED; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->dev.of_node = pdev->dev.of_node; r = i2c_dw_probe(dev); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index d8219bc2ac4e..c306751ceadb 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1257,6 +1257,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) priv->adapter.owner = THIS_MODULE; priv->adapter.class = i801_get_adapter_class(priv); priv->adapter.algo = &smbus_algorithm; + priv->adapter.dev.parent = &dev->dev; + ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&dev->dev)); + priv->adapter.retries = 3; priv->pci_dev = dev; switch (dev->device) { @@ -1388,12 +1391,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) i801_add_tco(priv); - /* set up the sysfs linkage to our parent device */ - priv->adapter.dev.parent = &dev->dev; - - /* Retry up to 3 times on lost arbitration */ - priv->adapter.retries = 3; - snprintf(priv->adapter.name, sizeof(priv->adapter.name), "SMBus I801 adapter at %04lx", priv->smba); err = i2c_add_adapter(&priv->adapter); diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 80648be36cce..ebff8205a000 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -842,17 +842,13 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; pci_set_drvdata(pdev, priv); + i2c_set_adapdata(&priv->adapter, priv); priv->adapter.owner = THIS_MODULE; - priv->adapter.class = I2C_CLASS_HWMON; - priv->adapter.algo = &smbus_algorithm; - - /* set up the sysfs linkage to our parent device */ priv->adapter.dev.parent = &pdev->dev; - - /* number of retries on lost arbitration */ + ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev)); priv->adapter.retries = ISMT_MAX_RETRIES; priv->pci_dev = pdev; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 3a4c54ef290d..5897fdb2480d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -156,7 +156,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, info.fwnode = acpi_fwnode_handle(adev); memset(&lookup, 0, sizeof(lookup)); - lookup.adapter_handle = ACPI_HANDLE(adapter->dev.parent); + lookup.adapter_handle = ACPI_HANDLE(&adapter->dev); lookup.device_handle = handle; lookup.info = &info; @@ -212,7 +212,7 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap) { acpi_status status; - if (!adap->dev.parent || !has_acpi_companion(adap->dev.parent)) + if (!has_acpi_companion(&adap->dev)) return; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 2ba7c0fbc615..00fc5b1c7b66 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -25,6 +25,7 @@ #include #include #include +#include /* multiplexer per channel data */ struct i2c_mux_priv { @@ -173,6 +174,13 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, } } + /* + * Associate the mux channel with an ACPI node. + */ + if (has_acpi_companion(mux_dev)) + acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(mux_dev), + chan_id); + if (force_nr) { priv->adap.nr = force_nr; ret = i2c_add_numbered_adapter(&priv->adap); -- cgit v1.2.3 From c18fba23061f16dde128e10d4869ba4e88e0e81a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 12 Nov 2015 15:42:26 +0200 Subject: i2c: fix wakeup irq parsing This patch fixes obvious copy-past error in wake up irq parsing code which leads to the fact that dev_pm_set_wake_irq() will be called with wrong IRQ number when "wakeup" IRQ is not defined in DT. Fixes: 3fffd1283927 ("i2c: allow specifying separate wakeup interrupt in device tree") Signed-off-by: Grygorii Strashko Acked-by: Dmitry Torokhov Signed-off-by: Wolfram Sang Cc: # v4.3 --- drivers/i2c/i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 040af5cc8143..ba8eb087f224 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -715,7 +715,7 @@ static int i2c_device_probe(struct device *dev) if (wakeirq > 0 && wakeirq != client->irq) status = dev_pm_set_dedicated_wake_irq(dev, wakeirq); else if (client->irq > 0) - status = dev_pm_set_wake_irq(dev, wakeirq); + status = dev_pm_set_wake_irq(dev, client->irq); else status = 0; -- cgit v1.2.3