[PATCH v4 2/2] platform/x86: x86-android-tablets: enable fwnode matching of GPIO chips
From: Bartosz Golaszewski
Date: Thu Apr 30 2026 - 03:35:32 EST
In order to allow GPIOLIB to match cherryview and baytrail GPIO
controllers by their firmware nodes instead of their names, we need to
attach the - currently "dangling" - existing software nodes to their
target devices dynamically.
The driver uses platform_create_bundle() and expects all required
providers to be present before it itself is probed. We know the name of
the device we're waiting for so look them up and assign the appropriate
software node as the secondary firmware node of the underlying ACPI node.
Scheduling fine-grained devres actions allows for proper teardown and
unsetting of the secondary firmware nodes.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/platform/x86/x86-android-tablets/core.c | 78 ++++++++++++++++++++++++-
1 file changed, 75 insertions(+), 3 deletions(-)
diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c
index 021009e9085bec3db9c4daa1f6235600210a6099..8fd0cffe351577182f31c90c104b616cec78c481 100644
--- a/drivers/platform/x86/x86-android-tablets/core.c
+++ b/drivers/platform/x86/x86-android-tablets/core.c
@@ -13,6 +13,7 @@
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/dmi.h>
+#include <linux/fwnode.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/irq.h>
@@ -360,6 +361,76 @@ static const struct software_node *cherryview_gpiochip_node_group[] = {
NULL
};
+static void auto_secondary_unset(void *data)
+{
+ struct fwnode_handle *fwnode = data;
+
+ fwnode->secondary = NULL;
+}
+
+static int acpi_set_secondary_fwnode(struct device *parent, struct device *dev,
+ const struct software_node *const swnode)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ fwnode = software_node_fwnode(swnode);
+ if (WARN_ON(!fwnode))
+ return -ENOENT;
+
+ fwnode->secondary = ERR_PTR(-ENODEV);
+ device->fwnode.secondary = fwnode;
+
+ ret = devm_add_action_or_reset(parent, auto_secondary_unset, &device->fwnode);
+ if (ret)
+ dev_err(parent, "Failed to schedule the unset action for secondary fwnode\n");
+
+ return ret;
+}
+
+static void auto_secondary_unregister_node_group(void *data)
+{
+ const struct software_node **nodes = data;
+
+ software_node_unregister_node_group(nodes);
+}
+
+static int auto_secondary_fwnode_init(struct device *parent)
+{
+ const struct software_node *const *swnode;
+ int ret;
+
+ if (!gpiochip_node_group)
+ return 0;
+
+ ret = software_node_register_node_group(gpiochip_node_group);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(parent,
+ auto_secondary_unregister_node_group,
+ gpiochip_node_group);
+ if (ret)
+ return ret;
+
+ for (swnode = gpiochip_node_group; *swnode; swnode++) {
+ struct device *dev __free(put_device) =
+ acpi_bus_find_device_by_name((*swnode)->name);
+ if (!dev) {
+ dev_err(parent, "Failed to find the required GPIO controller: %s\n",
+ (*swnode)->name);
+ return -ENODEV;
+ }
+
+ ret = acpi_set_secondary_fwnode(parent, dev, *swnode);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static void x86_android_tablet_remove(struct platform_device *pdev)
{
int i;
@@ -391,7 +462,6 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
software_node_unregister_node_group(gpio_button_swnodes);
software_node_unregister_node_group(swnode_group);
- software_node_unregister_node_group(gpiochip_node_group);
}
static __init int x86_android_tablet_probe(struct platform_device *pdev)
@@ -427,9 +497,11 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
break;
}
- ret = software_node_register_node_group(gpiochip_node_group);
- if (ret)
+ ret = auto_secondary_fwnode_init(&pdev->dev);
+ if (ret) {
+ x86_android_tablet_remove(pdev);
return ret;
+ }
ret = software_node_register_node_group(dev_info->swnode_group);
if (ret) {
--
2.47.3