Commit 8eb5c87a authored by Dustin Byford's avatar Dustin Byford Committed by Wolfram Sang

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: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarDustin Byford <dustin@cumulusnetworks.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent c0e5c445
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,,)
}
}
}
}
}
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/acpi.h>
#include "i2c-designware-core.h" #include "i2c-designware-core.h"
#define DRIVER_NAME "i2c-designware-pci" #define DRIVER_NAME "i2c-designware-pci"
...@@ -244,6 +245,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, ...@@ -244,6 +245,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
adap = &dev->adapter; adap = &dev->adapter;
adap->owner = THIS_MODULE; adap->owner = THIS_MODULE;
adap->class = 0; adap->class = 0;
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->nr = controller->bus_num; adap->nr = controller->bus_num;
r = i2c_dw_probe(dev); r = i2c_dw_probe(dev);
......
...@@ -207,6 +207,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) ...@@ -207,6 +207,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
adap = &dev->adapter; adap = &dev->adapter;
adap->owner = THIS_MODULE; adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED; adap->class = I2C_CLASS_DEPRECATED;
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->dev.of_node = pdev->dev.of_node; adap->dev.of_node = pdev->dev.of_node;
r = i2c_dw_probe(dev); r = i2c_dw_probe(dev);
......
...@@ -1257,6 +1257,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -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.owner = THIS_MODULE;
priv->adapter.class = i801_get_adapter_class(priv); priv->adapter.class = i801_get_adapter_class(priv);
priv->adapter.algo = &smbus_algorithm; 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; priv->pci_dev = dev;
switch (dev->device) { switch (dev->device) {
...@@ -1388,12 +1391,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -1388,12 +1391,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
i801_add_tco(priv); 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), snprintf(priv->adapter.name, sizeof(priv->adapter.name),
"SMBus I801 adapter at %04lx", priv->smba); "SMBus I801 adapter at %04lx", priv->smba);
err = i2c_add_adapter(&priv->adapter); err = i2c_add_adapter(&priv->adapter);
......
...@@ -842,17 +842,13 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -842,17 +842,13 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENOMEM; return -ENOMEM;
pci_set_drvdata(pdev, priv); pci_set_drvdata(pdev, priv);
i2c_set_adapdata(&priv->adapter, priv); i2c_set_adapdata(&priv->adapter, priv);
priv->adapter.owner = THIS_MODULE; priv->adapter.owner = THIS_MODULE;
priv->adapter.class = I2C_CLASS_HWMON; priv->adapter.class = I2C_CLASS_HWMON;
priv->adapter.algo = &smbus_algorithm; priv->adapter.algo = &smbus_algorithm;
/* set up the sysfs linkage to our parent device */
priv->adapter.dev.parent = &pdev->dev; priv->adapter.dev.parent = &pdev->dev;
ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev));
/* number of retries on lost arbitration */
priv->adapter.retries = ISMT_MAX_RETRIES; priv->adapter.retries = ISMT_MAX_RETRIES;
priv->pci_dev = pdev; priv->pci_dev = pdev;
......
...@@ -156,7 +156,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, ...@@ -156,7 +156,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
info.fwnode = acpi_fwnode_handle(adev); info.fwnode = acpi_fwnode_handle(adev);
memset(&lookup, 0, sizeof(lookup)); 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.device_handle = handle;
lookup.info = &info; lookup.info = &info;
...@@ -212,7 +212,7 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap) ...@@ -212,7 +212,7 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)
{ {
acpi_status status; acpi_status status;
if (!adap->dev.parent || !has_acpi_companion(adap->dev.parent)) if (!has_acpi_companion(&adap->dev))
return; return;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-mux.h> #include <linux/i2c-mux.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/acpi.h>
/* multiplexer per channel data */ /* multiplexer per channel data */
struct i2c_mux_priv { struct i2c_mux_priv {
...@@ -173,6 +174,13 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, ...@@ -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) { if (force_nr) {
priv->adap.nr = force_nr; priv->adap.nr = force_nr;
ret = i2c_add_numbered_adapter(&priv->adap); ret = i2c_add_numbered_adapter(&priv->adap);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment