From: Hardik Prakash <hardikprakash.official@gmail.com>
To: linux-i2c@vger.kernel.org
Cc: linux-gpio@vger.kernel.org, wsa@kernel.org,
andriy.shevchenko@intel.com, mario.limonciello@amd.com,
brgl@bgdev.pl, basavaraj.natikar@amd.com, linusw@kernel.org,
nathan@kernel.org, chaitanya.kumar.borah@intel.com,
Hardik Prakash <hardikprakash.official@gmail.com>
Subject: [PATCH v10] i2c: designware: defer probe if child GpioInt controllers are not bound
Date: Wed, 1 Jul 2026 15:42:18 +0530 [thread overview]
Message-ID: <20260701101218.42529-1-hardikprakash.official@gmail.com> (raw)
I2C controllers may have child devices with GpioInt resources that
depend on GPIO controllers to be fully initialized. If the I2C
controller probes and enumerates children before the referenced GPIO
controller has completed probe, GPIO interrupts may not be properly
configured, leading to device failures.
On Lenovo Yoga 7 14AGP11, the WACF2200 touchscreen (child of
AMDI0010:02) has a GpioInt resource pointing to GPIO 157 on the
pinctrl-amd controller (AMDI0030:00). When i2c-designware probes
AMDI0010:02 before pinctrl-amd finishes initializing, I2C transactions
fail with lost arbitration errors.
Add a generic dependency check in i2c-designware that walks ACPI child
devices, identifies any GpioInt resources, resolves the referenced GPIO
controllers, and defers probe if those controllers are not yet bound.
Uses acpi_gpio_get_irq_resource() to avoid duplicating GPIO resource
parsing logic from gpiolib-acpi. Skips resources with no resource
source string (string_length == 0 or string_ptr == NULL) to avoid
crashes on hardware where GPIO resources have no named controller.
The probe ordering race was confirmed via dynamic debug tracing:
0.285952 amd_gpio_probe: registering gpiochip <- GPIO chip visible
0.287121 amd_gpio_probe: requesting parent IRQ <- probe still running
0.301454 AMDI0010:02 dw_i2c_plat_probe: start <- races here
2.348157 lost arbitration
Fixes: 3812a9e84265 ("pinctrl-amd: enable IRQ for WACF2200 touchscreen on Lenovo Yoga 7 14AGP11")
Suggested-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Hardik Prakash <hardikprakash.official@gmail.com>
Assisted-by: Claude:claude-sonnet-4-6
Assisted-by: GPT:gpt-5.4-mini
Link: https://bugzilla.kernel.org/show_bug.cgi?id=221494
---
drivers/i2c/busses/i2c-designware-platdrv.c | 132 ++++++++++++++++++++
1 file changed, 132 insertions(+)
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 6d6e81242f74..d8331d1dead8 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -8,6 +8,8 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*/
+
+#include <linux/acpi.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -130,6 +132,132 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
return 0;
}
+#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
+struct gpio_controller_ref {
+ struct list_head node;
+ const char *path;
+};
+
+static void free_gpio_controller_list(struct list_head *gpio_controllers)
+{
+ struct gpio_controller_ref *ref, *tmp;
+
+ list_for_each_entry_safe(ref, tmp, gpio_controllers, node) {
+ list_del(&ref->node);
+ kfree(ref->path);
+ kfree(ref);
+ }
+}
+
+static int check_gpioint_resource(struct acpi_resource *ares, void *data)
+{
+ struct list_head *gpio_controllers = data;
+ struct acpi_resource_gpio *agpio;
+ struct gpio_controller_ref *ref;
+
+ if (!acpi_gpio_get_irq_resource(ares, &agpio))
+ return 1;
+
+ if (!agpio->resource_source.string_length)
+ return 1;
+
+ /* Skip if we've already tracked this GPIO controller */
+ list_for_each_entry(ref, gpio_controllers, node) {
+ if (!strncmp(ref->path, agpio->resource_source.string_ptr,
+ agpio->resource_source.string_length))
+ return 1;
+ }
+
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+
+ ref->path = kstrdup(agpio->resource_source.string_ptr, GFP_KERNEL);
+ if (!ref->path) {
+ kfree(ref);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&ref->node, gpio_controllers);
+ return 1;
+}
+
+static int check_child_gpioint(struct acpi_device *adev, void *data)
+{
+ struct list_head res_list;
+ int ret;
+
+ INIT_LIST_HEAD(&res_list);
+ ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, data);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&res_list);
+ return 0;
+}
+
+static int i2c_dw_check_gpio_dependencies(struct device *dev)
+{
+ struct gpio_controller_ref *ref;
+ LIST_HEAD(gpio_controllers);
+ struct acpi_device *adev;
+ int ret;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return 0;
+
+ /* Walk all child devices and collect GpioInt controller references */
+ ret = acpi_dev_for_each_child(adev, check_child_gpioint, &gpio_controllers);
+ if (ret < 0)
+ goto cleanup;
+
+ /* For each GPIO controller, check if its platform device is bound */
+ list_for_each_entry(ref, &gpio_controllers, node) {
+ struct acpi_device *gpio_adev;
+ struct device *gpio_dev;
+ acpi_status status;
+ acpi_handle handle;
+ bool bound;
+
+ status = acpi_get_handle(NULL, ref->path, &handle);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ gpio_adev = acpi_fetch_acpi_dev(handle);
+ if (!gpio_adev)
+ continue;
+
+ gpio_dev = acpi_get_first_physical_node(gpio_adev);
+ acpi_dev_put(gpio_adev);
+ if (gpio_dev) {
+ guard(device)(gpio_dev);
+ bound = device_is_bound(gpio_dev);
+ } else {
+ bound = false;
+ }
+ /*
+ * Defer probe until the GPIO controller is fully bound,
+ * ensuring its IRQ setup is complete before we enumerate
+ * I2C child devices.
+ */
+ if (!bound) {
+ ret = -EPROBE_DEFER;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ free_gpio_controller_list(&gpio_controllers);
+ return ret;
+}
+#else
+static int i2c_dw_check_gpio_dependencies(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */
+
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
@@ -138,6 +266,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
struct dw_i2c_dev *dev;
int irq, ret;
+ ret = i2c_dw_check_gpio_dependencies(device);
+ if (ret)
+ return ret;
+
irq = platform_get_irq_optional(pdev, 0);
if (irq == -ENXIO)
flags |= ACCESS_POLLING;
base-commit: 5a66900afbd6b2a063eebad35294038a654de2b0
--
2.54.0
reply other threads:[~2026-07-01 10:12 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260701101218.42529-1-hardikprakash.official@gmail.com \
--to=hardikprakash.official@gmail.com \
--cc=andriy.shevchenko@intel.com \
--cc=basavaraj.natikar@amd.com \
--cc=brgl@bgdev.pl \
--cc=chaitanya.kumar.borah@intel.com \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=mario.limonciello@amd.com \
--cc=nathan@kernel.org \
--cc=wsa@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox