diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index da9826accb1b..269d20d51936 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -102,4 +102,17 @@ config OF_OVERLAY
 config OF_NUMA
 	bool
 
+config HW_PROBER
+	bool "Hardware Prober driver"
+	select I2C
+	select OF_DYNAMIC
+	help
+	  Some devices will have multiple drop-in options for one component.
+	  In many cases the different options are indistinguishable by the
+	  kernel without actually probing each possible option.
+
+	  This driver is meant to handle the probing of such components, and
+	  update the running device tree such that the correct variant is
+	  made available.
+
 endif # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index eff624854575..ed3875cdc554 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
 obj-$(CONFIG_OF_RESOLVE)  += resolver.o
 obj-$(CONFIG_OF_OVERLAY) += overlay.o
 obj-$(CONFIG_OF_NUMA) += of_numa.o
+obj-$(CONFIG_HW_PROBER) += hw_prober.o
 
 ifdef CONFIG_KEXEC_FILE
 ifdef CONFIG_OF_FLATTREE
diff --git a/drivers/of/hw_prober.c b/drivers/of/hw_prober.c
new file mode 100644
index 000000000000..442da6eff896
--- /dev/null
+++ b/drivers/of/hw_prober.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hw_prober.c - Hardware prober driver
+ *
+ * Copyright (c) 2023 Google LLC
+ */
+
+#include <linux/array_size.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define DRV_NAME	"hw_prober"
+
+/**
+ * struct hw_prober_entry - Holds an entry for the hardware prober
+ *
+ * @compatible:	compatible string to match against the machine
+ * @prober:	prober function to call when machine matches
+ * @data:	extra data for the prober function
+ */
+struct hw_prober_entry {
+	const char *compatible;
+	int (*prober)(struct platform_device *pdev, const void *data);
+	const void *data;
+};
+
+/*
+ * Some devices, such as Google Hana Chromebooks, are produced by multiple
+ * vendors each using their preferred components. This prober assumes such
+ * drop-in parts are on dedicated I2C busses, have non-conflicting addresses,
+ * and can be directly probed by seeing which address responds without needing
+ * regulators or GPIOs being enabled or toggled.
+ */
+static int i2c_component_prober(struct platform_device *pdev, const void *data)
+{
+	const char *node_name = data;
+	struct device_node *node, *i2c_node;
+	struct i2c_adapter *i2c;
+	int ret = 0;
+
+	node = of_find_node_by_name(NULL, node_name);
+	if (!node)
+		return dev_err_probe(&pdev->dev, -ENODEV, "Could not find %s device node\n",
+				     node_name);
+
+	i2c_node = of_get_next_parent(node);
+	if (strcmp(i2c_node->name, "i2c")) {
+		of_node_put(i2c_node);
+		return dev_err_probe(&pdev->dev, -EINVAL, "%s device isn't on I2C bus\n",
+				     node_name);
+	}
+
+	for_each_child_of_node(i2c_node, node) {
+		if (!of_node_name_prefix(node, node_name))
+			continue;
+		if (!of_device_is_fail(node)) {
+			/* device tree has component already enabled */
+			of_node_put(node);
+			of_node_put(i2c_node);
+			return 0;
+		}
+	}
+
+	i2c = of_get_i2c_adapter_by_node(i2c_node);
+	if (!i2c) {
+		of_node_put(i2c_node);
+		return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "Couldn't get I2C adapter\n");
+	}
+
+	for_each_child_of_node(i2c_node, node) {
+		struct property *prop;
+		union i2c_smbus_data data;
+		u32 addr;
+
+		if (!of_node_name_prefix(node, node_name))
+			continue;
+		if (of_property_read_u32(node, "reg", &addr))
+			continue;
+		if (i2c_smbus_xfer(i2c, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data) < 0)
+			continue;
+
+		dev_info(&pdev->dev, "Enabling %pOF\n", node);
+
+		prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+		if (!prop) {
+			ret = -ENOMEM;
+			of_node_put(node);
+			break;
+		}
+
+		prop->name	= "status";
+		prop->length	= 5;
+		prop->value	= "okay";
+
+		/* Found a device that is responding */
+		ret = of_update_property(node, prop);
+		if (ret)
+			kfree(prop);
+
+		of_node_put(node);
+		break;
+	}
+
+	i2c_put_adapter(i2c);
+	of_node_put(i2c_node);
+
+	return ret;
+}
+
+static const struct hw_prober_entry hw_prober_platforms[] = {
+	{ .compatible = "google,hana", .prober = i2c_component_prober, .data = "touchscreen" },
+	{ .compatible = "google,hana", .prober = i2c_component_prober, .data = "trackpad" },
+};
+
+static int hw_prober_probe(struct platform_device *pdev)
+{
+	for (int i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++)
+		if (of_machine_is_compatible(hw_prober_platforms[i].compatible)) {
+			int ret;
+
+			ret = hw_prober_platforms[i].prober(pdev, hw_prober_platforms[i].data);
+			if (ret)
+				return ret;
+		}
+
+	return 0;
+}
+
+static struct platform_driver hw_prober_driver = {
+	.probe	= hw_prober_probe,
+	.driver	= {
+		.name = DRV_NAME,
+	},
+};
+
+static int __init hw_prober_driver_init(void)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	ret = platform_driver_register(&hw_prober_driver);
+	if (ret)
+		return ret;
+
+	pdev = platform_device_register_simple(DRV_NAME, -1, NULL, 0);
+	if (!IS_ERR(pdev))
+		return 0;
+
+	platform_driver_unregister(&hw_prober_driver);
+
+	return PTR_ERR(pdev);
+}
+device_initcall(hw_prober_driver_init);
