[PATCH v2] i2c: i2c-mux-gpio: Enable this driver in ACPI land

From: Evan Green
Date: Wed Oct 14 2020 - 21:19:46 EST


Enable i2c-mux-gpio devices to be defined via ACPI. The idle-state
property translates directly to a fwnode_property_*() call. The child
reg property translates naturally into _ADR in ACPI.

The i2c-parent binding is a relic from the days when all direct children
of an i2c controller in Linux had to be i2c devices. These days that
implementation detail has been worked out, so the i2c-mux can sit
as a direct child of its parent controller, which is where it makes the
most sense from a hardware description perspective. For the ACPI
implementation we'll assume that's always how the i2c-mux-gpio is
instantiated.

Signed-off-by: Evan Green <evgreen@xxxxxxxxxxxx>
---

Changes in v2:
- Make it compile properly when !CONFIG_ACPI (Randy)
- Update commit message regarding i2c-parent (Peter)

drivers/i2c/muxes/i2c-mux-gpio.c | 103 ++++++++++++++++++++++---------
1 file changed, 75 insertions(+), 28 deletions(-)

diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 4effe563e9e8d..8e4008f4a9b5d 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -49,34 +49,80 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
return 0;
}

-#ifdef CONFIG_OF
-static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
- struct platform_device *pdev)
+#ifdef CONFIG_ACPI
+
+static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
+ struct fwnode_handle *fwdev,
+ unsigned int *adr)
+
+{
+ unsigned long long adr64;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
+ METHOD_NAME__ADR,
+ NULL, &adr64);
+
+ if (!ACPI_SUCCESS(status)) {
+ dev_err(dev, "Cannot get address");
+ return -EINVAL;
+ }
+
+ *adr = adr64;
+ return 0;
+}
+
+#else
+
+static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
+ struct fwnode_handle *fwdev,
+ unsigned int *adr)
+{
+ return -EINVAL;
+}
+
+#endif
+
+static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
+ struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *adapter_np, *child;
- struct i2c_adapter *adapter;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ acpi_handle dev_handle;
+ struct device_node *adapter_np;
+ struct i2c_adapter *adapter = NULL;
+ struct fwnode_handle *child = NULL;
unsigned *values;
- int i = 0;
+ int rc, i = 0;

- if (!np)
- return -ENODEV;
+ if (is_of_node(dev->fwnode)) {
+ if (!np)
+ return -ENODEV;

- adapter_np = of_parse_phandle(np, "i2c-parent", 0);
- if (!adapter_np) {
- dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
- return -ENODEV;
+ adapter_np = of_parse_phandle(np, "i2c-parent", 0);
+ if (!adapter_np) {
+ dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
+ return -ENODEV;
+ }
+ adapter = of_find_i2c_adapter_by_node(adapter_np);
+ of_node_put(adapter_np);
+
+ } else if (is_acpi_node(dev->fwnode)) {
+ /*
+ * In ACPI land the mux should be a direct child of the i2c
+ * bus it muxes.
+ */
+ dev_handle = ACPI_HANDLE(dev->parent);
+ adapter = i2c_acpi_find_adapter_by_handle(dev_handle);
}
- adapter = of_find_i2c_adapter_by_node(adapter_np);
- of_node_put(adapter_np);
+
if (!adapter)
return -EPROBE_DEFER;

mux->data.parent = i2c_adapter_id(adapter);
put_device(&adapter->dev);

- mux->data.n_values = of_get_child_count(np);
-
+ mux->data.n_values = device_get_child_node_count(dev);
values = devm_kcalloc(&pdev->dev,
mux->data.n_values, sizeof(*mux->data.values),
GFP_KERNEL);
@@ -85,24 +131,25 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
return -ENOMEM;
}

- for_each_child_of_node(np, child) {
- of_property_read_u32(child, "reg", values + i);
+ device_for_each_child_node(dev, child) {
+ if (is_of_node(child)) {
+ fwnode_property_read_u32(child, "reg", values + i);
+
+ } else if (is_acpi_node(child)) {
+ rc = i2c_mux_gpio_get_acpi_adr(dev, child, values + i);
+ if (rc)
+ return rc;
+ }
+
i++;
}
mux->data.values = values;

- if (of_property_read_u32(np, "idle-state", &mux->data.idle))
+ if (fwnode_property_read_u32(dev->fwnode, "idle-state", &mux->data.idle))
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;

return 0;
}
-#else
-static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
- struct platform_device *pdev)
-{
- return 0;
-}
-#endif

static int i2c_mux_gpio_probe(struct platform_device *pdev)
{
@@ -118,7 +165,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -ENOMEM;

if (!dev_get_platdata(&pdev->dev)) {
- ret = i2c_mux_gpio_probe_dt(mux, pdev);
+ ret = i2c_mux_gpio_probe_fw(mux, pdev);
if (ret < 0)
return ret;
} else {
--
2.26.2