Some devices are designed and manufactured with some components having
multiple drop-in replacement options. These components are often
connected to the mainboard via ribbon cables, having the same signals
and pin assignments across all options. These may include the display
panel and touchscreen on laptops and tablets, and the trackpad on
laptops. Sometimes which component option is used in a particular device
can be detected by some firmware provided identifier, other times that
information is not available, and the kernel has to try to probe each
device.
This change attempts to make the "probe each device" case cleaner. The
current approach is to have all options added and enabled in the device
tree. The kernel would then bind each device and run each driver's probe
function. This works, but has been broken before due to the introduction
of asynchronous probing, causing multiple instances requesting "shared"
resources, such as pinmuxes, GPIO pins, interrupt lines, at the same
time, with only one instance succeeding. Work arounds for these include
moving the pinmux to the parent I2C controller, using GPIO hogs or
pinmux settings to keep the GPIO pins in some fixed configuration, and
requesting the interrupt line very late. Such configurations can be seen
on the MT8183 Krane Chromebook tablets, and the Qualcomm sc8280xp-based
Lenovo Thinkpad 13S.
Instead of this delicate dance between drivers and device tree quirks,
this change introduces a simple I2C component probe function. For a
given class of devices on the same I2C bus, it will go through all of
them, doing a simple I2C read transfer and see which one of them responds.
It will then enable the device that responds.
This requires some minor modifications in the existing device tree. The
status for all the device nodes for the component options must be set
to "fail-needs-probe". This makes it clear that some mechanism is
needed to enable one of them, and also prevents the prober and device
drivers running at the same time.
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v7:
- Dropped log level of "enabling component" to debug
- Dropped file name from header file
- Reverted to __free() cleanup for i2c bus node
- Corrected "failed-needs-probe" to "fail-needs-probe" in commit message
- Fixed incorrectly positioned period ('.') in commit message
- Expanded description of i2c_of_probe_component()
- Expanded comment explaining check for "available" devices to note that
if such a device is found then the i2c probe function becomes a no-op
- Simplified check for "available" devices for-each loop
- Expanded description of @free_resources_early callback to explicitly
state that it is not called if no working components are found
- Dropped !cfg check
- Replaced "fail" with "fail-needs-probe" in i2c_of_probe_component()
kernel doc
- Combined callbacks (.get_resources with .enable; .cleanup with
.free_resources_late); .free_resources_early renamed to .cleanup_early
Changes since v6:
- Correctly replaced for_each_child_of_node_scoped() with
for_each_child_of_node_with_prefix()
- Added namespace for exported symbol
- Made the probe function a framework with hooks
- Split out a new header file
- Added MAINTAINERS entry
- Reworded kernel-doc
- Dropped usage of __free from i2c_of_probe_component() since error
path cleanup is needed anyway
Changes since v5:
- Fixed indent in Makefile
- Split regulator and GPIO TODO items
- Reversed final conditional in i2c_of_probe_enable_node()
Changes since v4:
- Split code into helper functions
- Use scoped helpers and __free() to reduce error path
Changes since v3:
- Complete kernel-doc
- Return different error if I2C controller is disabled
- Expand comment to explain assumptions and constraints
- Split for-loop finding target node and operations on target node
- Add missing i2c_put_adapter()
- Move prober code to separate file
Rob also asked why there was a limitation of "exactly one touchscreen
will be enabled across the whole tree".
The use case this prober currently targets is a component on consumer
electronics (tablet or laptop) being swapped out due to cost or supply
reasons. Designs with multiple components of the same type are pretty
rare. The way the next patch is written also assumes this for efficiency
reasons.
Changes since v2:
- New patch split out from "of: Introduce hardware prober driver"
- Addressed Rob's comments
- Move i2c prober to i2c subsystem
- Use of_node_is_available() to check if node is enabled.
- Use OF changeset API to update status property
- Addressed Andy's comments
- Probe function now accepts "struct device *dev" instead to reduce
line length and dereferences
- Move "ret = 0" to just before for_each_child_of_node(i2c_node, node)
---
MAINTAINERS | 8 ++
drivers/i2c/Makefile | 1 +
drivers/i2c/i2c-core-of-prober.c | 176 +++++++++++++++++++++++++++++++
include/linux/i2c-of-prober.h | 70 ++++++++++++
4 files changed, 255 insertions(+)
create mode 100644 drivers/i2c/i2c-core-of-prober.c
create mode 100644 include/linux/i2c-of-prober.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 84086d47db69..5defa175a6bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10682,6 +10682,14 @@ S: Maintained
F: Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml
F: drivers/i2c/busses/i2c-mv64xxx.c
+I2C OF COMPONENT PROBER
+M: Chen-Yu Tsai <wenst@chromium.org>
+L: linux-i2c@vger.kernel.org
+L: devicetree@vger.kernel.org
+S: Maintained
+F: drivers/i2c/i2c-core-of-prober.c
+F: include/linux-i2c-of-prober.h
+
I2C OVER PARALLEL PORT
M: Jean Delvare <jdelvare@suse.com>
L: linux-i2c@vger.kernel.org
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index f12d6b10a85e..c539cdc1e305 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -9,6 +9,7 @@ i2c-core-objs := i2c-core-base.o i2c-core-smbus.o
i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o
i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o
i2c-core-$(CONFIG_OF) += i2c-core-of.o
+i2c-core-$(CONFIG_OF_DYNAMIC) += i2c-core-of-prober.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
new file mode 100644
index 000000000000..cc1aae9fef43
--- /dev/null
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linux I2C core OF component prober code
+ *
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c-of-prober.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/*
+ * Some devices, such as Google Hana Chromebooks, are produced by multiple
+ * vendors each using their preferred components. Such components are all
+ * in the device tree. Instead of having all of them enabled and having each
+ * driver separately try and probe its device while fighting over shared
+ * resources, they can be marked as "fail-needs-probe" and have a prober
+ * figure out which one is actually used beforehand.
+ *
+ * This prober assumes such drop-in parts are on the same I2C bus, have
+ * non-conflicting addresses, and can be directly probed by seeing which
+ * address responds.
+ *
+ * TODO:
+ * - Support handling common regulators.
+ * - Support handling common GPIOs.
+ * - Support I2C muxes
+ */
+
+static struct device_node *i2c_of_probe_get_i2c_node(struct device *dev, const char *type)
+{
+ struct device_node *node __free(device_node) = of_find_node_by_name(NULL, type);
+ if (!node)
+ return dev_err_ptr_probe(dev, -ENODEV, "Could not find %s device node\n", type);
+
+ struct device_node *i2c_node __free(device_node) = of_get_parent(node);
+ if (!of_node_name_eq(i2c_node, "i2c"))
+ return dev_err_ptr_probe(dev, -EINVAL, "%s device isn't on I2C bus\n", type);
+
+ if (!of_device_is_available(i2c_node))
+ return dev_err_ptr_probe(dev, -ENODEV, "I2C controller not available\n");
+
+ return no_free_ptr(i2c_node);
+}
+
+static int i2c_of_probe_enable_node(struct device *dev, struct device_node *node)
+{
+ int ret;
+
+ dev_dbg(dev, "Enabling %pOF\n", node);
+
+ struct of_changeset *ocs __free(kfree) = kzalloc(sizeof(*ocs), GFP_KERNEL);
+ if (!ocs)
+ return -ENOMEM;
+
+ of_changeset_init(ocs);
+ ret = of_changeset_update_prop_string(ocs, node, "status", "okay");
+ if (ret)
+ return ret;
+
+ ret = of_changeset_apply(ocs);
+ if (ret) {
+ /* ocs needs to be explicitly cleaned up before being freed. */
+ of_changeset_destroy(ocs);
+ } else {
+ /*
+ * ocs is intentionally kept around as it needs to
+ * exist as long as the change is applied.
+ */
+ void *ptr __always_unused = no_free_ptr(ocs);
+ }
+
+ return ret;
+}
+
+static const struct i2c_of_probe_ops i2c_of_probe_dummy_ops;
+
+/**
+ * i2c_of_probe_component() - probe for devices of "type" on the same i2c bus
+ * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages.
+ * @cfg: Pointer to the &struct i2c_of_probe_cfg containing callbacks and other options
+ * for the prober.
+ * @ctx: Context data for callbacks.
+ *
+ * Probe for possible I2C components of the same "type" (&i2c_of_probe_cfg->type)
+ * on the same I2C bus that have their status marked as "fail-needs-probe".
+ *
+ * Assumes that across the entire device tree the only instances of nodes
+ * with "type" prefixed node names (not including the address portion) are
+ * the ones that need handling for second source components. In other words,
+ * if "type" is "touchscreen", then all device nodes named "touchscreen*"
+ * are the ones that need probing. There cannot be another "touchscreen*"
+ * node that is already enabled.
+ *
+ * Assumes that for each "type" of component, only one actually exists. In
+ * other words, only one matching and existing device will be enabled.
+ *
+ * Context: Process context only. Does non-atomic I2C transfers.
+ * Should only be used from a driver probe function, as the function
+ * can return -EPROBE_DEFER if the I2C adapter or other resources
+ * are unavailable.
+ * Return: 0 on success or no-op, error code otherwise.
+ * A no-op can happen when it seems like the device tree already
+ * has components of the type to be probed already enabled. This
+ * can happen when the device tree had not been updated to mark
+ * the status of the to-be-probed components as "fail-needs-probe".
+ * Or this function was already run with the same parameters and
+ * succeeded in enabling a component. The latter could happen if
+ * the user had multiple types of components to probe, and one of
+ * them down the list caused a deferred probe. This is expected
+ * behavior.
+ */
+int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx)
+{
+ const struct i2c_of_probe_ops *ops;
+ const char *type;
+ struct i2c_adapter *i2c;
+ int ret;
+
+ ops = cfg->ops ?: &i2c_of_probe_dummy_ops;
+ type = cfg->type;
+
+ struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type);
+ if (IS_ERR(i2c_node))
+ return PTR_ERR(i2c_node);
+
+ /*
+ * If any devices of the given "type" are already enabled then this function is a no-op.
+ * Either the device tree hasn't been modified to work with this probe function, or the
+ * function had already run before and enabled some component.
+ */
+ for_each_child_of_node_with_prefix(i2c_node, node, type)
+ if (of_device_is_available(node))
+ return 0;
+
+ i2c = of_get_i2c_adapter_by_node(i2c_node);
+ if (!i2c)
+ return dev_err_probe(dev, -EPROBE_DEFER, "Couldn't get I2C adapter\n");
+
+ /* Grab and enable resources */
+ ret = 0;
+ if (ops->enable)
+ ret = ops->enable(dev, i2c_node, ctx);
+ if (ret)
+ goto out_put_i2c_adapter;
+
+ for_each_child_of_node_with_prefix(i2c_node, node, type) {
+ union i2c_smbus_data data;
+ u32 addr;
+
+ 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;
+
+ /* Found a device that is responding */
+ if (ops->cleanup_early)
+ ops->cleanup_early(dev, ctx);
+ ret = i2c_of_probe_enable_node(dev, node);
+ break;
+ }
+
+ if (ops->cleanup)
+ ops->cleanup(dev, ctx);
+out_put_i2c_adapter:
+ i2c_put_adapter(i2c);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_component, I2C_OF_PROBER);
diff --git a/include/linux/i2c-of-prober.h b/include/linux/i2c-of-prober.h
new file mode 100644
index 000000000000..b771da21a051
--- /dev/null
+++ b/include/linux/i2c-of-prober.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Definitions for the Linux I2C OF component prober
+ *
+ * Copyright (C) 2024 Google LLC
+ */
+
+#ifndef _LINUX_I2C_OF_PROBER_H
+#define _LINUX_I2C_OF_PROBER_H
+
+struct device;
+struct device_node;
+
+/**
+ * struct i2c_of_probe_ops - I2C OF component prober callbacks
+ *
+ * A set of callbacks to be used by i2c_of_probe_component().
+ *
+ * All callbacks are optional. Callbacks are called only once per run, and are
+ * used in the order they are defined in this structure.
+ *
+ * All callbacks that have return values shall return %0 on success,
+ * or a negative error number on failure.
+ *
+ * The @dev parameter passed to the callbacks is the same as @dev passed to
+ * i2c_of_probe_component(). It should only be used for dev_printk() calls
+ * and nothing else, especially not managed device resource (devres) APIs.
+ */
+struct i2c_of_probe_ops {
+ /**
+ * @enable: Retrieve and enable resources so that the components respond to probes.
+ *
+ * Resources should be reverted to their initial state before returning if this fails.
+ */
+ int (*enable)(struct device *dev, struct device_node *bus_node, void *data);
+
+ /**
+ * @cleanup_early: Release exclusive resources prior to enabling component.
+ *
+ * Only called if a matching component is actually found. If none are found,
+ * resources that would have been released in this callback should be released in
+ * @free_resourcs_late instead.
+ */
+ void (*cleanup_early)(struct device *dev, void *data);
+
+ /**
+ * @cleanup: Opposite of @enable to balance refcounts and free resources after probing.
+ *
+ * Should check if resources were already freed by @cleanup_early.
+ */
+ void (*cleanup)(struct device *dev, void *data);
+};
+
+/**
+ * struct i2c_of_probe_cfg - I2C OF component prober configuration
+ * @ops: Callbacks for the prober to use.
+ * @type: A string to match the device node name prefix to probe for.
+ */
+struct i2c_of_probe_cfg {
+ const struct i2c_of_probe_ops *ops;
+ const char *type;
+};
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+
+int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx);
+
+#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
+
+#endif /* _LINUX_I2C_OF_PROBER_H */
--
2.47.0.rc0.187.ge670bccf7e-goog
Hi, On Tue, Oct 8, 2024 at 12:35 AM Chen-Yu Tsai <wenst@chromium.org> wrote: > > +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx) > +{ > + const struct i2c_of_probe_ops *ops; > + const char *type; > + struct i2c_adapter *i2c; > + int ret; > + > + ops = cfg->ops ?: &i2c_of_probe_dummy_ops; > + type = cfg->type; > + > + struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type); > + if (IS_ERR(i2c_node)) > + return PTR_ERR(i2c_node); I'm still getting comfortable with the __free() syntax so sorry if I'm wrong, but I _think_ the above is buggy. I believe that the definition of the free function for "device_node" is from: DEFINE_FREE(device_node, struct device_node *, if (_T) of_node_put(_T)) ...which means it'll call of_node_put() to free "i2c_node" when it goes out of scope. of_node_put() handles NULL pointers but _not_ ERR pointers. So I think that if you get an error back and then return via the PTR_ERR(i2c_node) then it'll crash because it will try to free an ERR pointer. Did I get that right? Presumably you need to instead do: return PTR_ERR(no_free_ptr(i2c_node)); ...or change of_node_put() to be a noop for error pointers? > +struct i2c_of_probe_ops { > + /** > + * @enable: Retrieve and enable resources so that the components respond to probes. > + * > + * Resources should be reverted to their initial state before returning if this fails. nit: might be worth explicitly noting that returning -EPROBE_DEFER is OK here because this both retrieves resources and enables. > + */ > + int (*enable)(struct device *dev, struct device_node *bus_node, void *data); > + > + /** > + * @cleanup_early: Release exclusive resources prior to enabling component. nit: change the word "enabling" to "calling probe() on a detected" since otherwise it could be confused with the "enable" function above. -Doug
On Wed, Oct 16, 2024 at 1:58 AM Doug Anderson <dianders@chromium.org> wrote: > > Hi, > > On Tue, Oct 8, 2024 at 12:35 AM Chen-Yu Tsai <wenst@chromium.org> wrote: > > > > +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx) > > +{ > > + const struct i2c_of_probe_ops *ops; > > + const char *type; > > + struct i2c_adapter *i2c; > > + int ret; > > + > > + ops = cfg->ops ?: &i2c_of_probe_dummy_ops; > > + type = cfg->type; > > + > > + struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type); > > + if (IS_ERR(i2c_node)) > > + return PTR_ERR(i2c_node); > > I'm still getting comfortable with the __free() syntax so sorry if I'm > wrong, but I _think_ the above is buggy. I believe that the definition > of the free function for "device_node" is from: > > DEFINE_FREE(device_node, struct device_node *, if (_T) of_node_put(_T)) > > ...which means it'll call of_node_put() to free "i2c_node" when it > goes out of scope. of_node_put() handles NULL pointers but _not_ ERR > pointers. So I think that if you get an error back and then return via > the PTR_ERR(i2c_node) then it'll crash because it will try to free an > ERR pointer. Did I get that right? Presumably you need to instead do: > > return PTR_ERR(no_free_ptr(i2c_node)); > > ...or change of_node_put() to be a noop for error pointers? Good catch! As Andy suggested, it should be updated to handle both. I'll add a patch for this. > > +struct i2c_of_probe_ops { > > + /** > > + * @enable: Retrieve and enable resources so that the components respond to probes. > > + * > > + * Resources should be reverted to their initial state before returning if this fails. > > nit: might be worth explicitly noting that returning -EPROBE_DEFER is > OK here because this both retrieves resources and enables. Makes sense. Will do. > > + */ > > + int (*enable)(struct device *dev, struct device_node *bus_node, void *data); > > + > > + /** > > + * @cleanup_early: Release exclusive resources prior to enabling component. > > nit: change the word "enabling" to "calling probe() on a detected" > since otherwise it could be confused with the "enable" function above. Makes sense. Will do. ChenYu
On Wed, Oct 16, 2024 at 3:01 PM Chen-Yu Tsai <wenst@chromium.org> wrote: > > On Wed, Oct 16, 2024 at 1:58 AM Doug Anderson <dianders@chromium.org> wrote: > > > > Hi, > > > > On Tue, Oct 8, 2024 at 12:35 AM Chen-Yu Tsai <wenst@chromium.org> wrote: > > > > > > +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx) > > > +{ > > > + const struct i2c_of_probe_ops *ops; > > > + const char *type; > > > + struct i2c_adapter *i2c; > > > + int ret; > > > + > > > + ops = cfg->ops ?: &i2c_of_probe_dummy_ops; > > > + type = cfg->type; > > > + > > > + struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type); > > > + if (IS_ERR(i2c_node)) > > > + return PTR_ERR(i2c_node); > > > > I'm still getting comfortable with the __free() syntax so sorry if I'm > > wrong, but I _think_ the above is buggy. I believe that the definition > > of the free function for "device_node" is from: > > > > DEFINE_FREE(device_node, struct device_node *, if (_T) of_node_put(_T)) > > > > ...which means it'll call of_node_put() to free "i2c_node" when it > > goes out of scope. of_node_put() handles NULL pointers but _not_ ERR > > pointers. So I think that if you get an error back and then return via > > the PTR_ERR(i2c_node) then it'll crash because it will try to free an > > ERR pointer. Did I get that right? Presumably you need to instead do: > > > > return PTR_ERR(no_free_ptr(i2c_node)); > > > > ...or change of_node_put() to be a noop for error pointers? > > Good catch! As Andy suggested, it should be updated to handle both. > I'll add a patch for this. On second thought, it might be better to change i2c_of_probe_get_i2c_node() to return NULL on errors. That seems to be what most functions do. I only found a handful of exceptions. Rob, any thoughts from your end? ChenYu
On Wed, Oct 16, 2024 at 05:28:05PM +0800, Chen-Yu Tsai wrote: > On Wed, Oct 16, 2024 at 3:01 PM Chen-Yu Tsai <wenst@chromium.org> wrote: > > On Wed, Oct 16, 2024 at 1:58 AM Doug Anderson <dianders@chromium.org> wrote: > > > On Tue, Oct 8, 2024 at 12:35 AM Chen-Yu Tsai <wenst@chromium.org> wrote: ... > > > ...which means it'll call of_node_put() to free "i2c_node" when it > > > goes out of scope. of_node_put() handles NULL pointers but _not_ ERR > > > pointers. So I think that if you get an error back and then return via > > > the PTR_ERR(i2c_node) then it'll crash because it will try to free an > > > ERR pointer. Did I get that right? Presumably you need to instead do: > > > > > > return PTR_ERR(no_free_ptr(i2c_node)); > > > > > > ...or change of_node_put() to be a noop for error pointers? > > > > Good catch! As Andy suggested, it should be updated to handle both. > > I'll add a patch for this. > > On second thought, it might be better to change i2c_of_probe_get_i2c_node() > to return NULL on errors. That seems to be what most functions do. I only > found a handful of exceptions. It seems that OF has been written in the assumption that device node pointer is never an error pointer. So, probably fixing your function is the best approach right now. -- With Best Regards, Andy Shevchenko
On Tue, Oct 15, 2024 at 10:58:31AM -0700, Doug Anderson wrote: > On Tue, Oct 8, 2024 at 12:35 AM Chen-Yu Tsai <wenst@chromium.org> wrote: ... > > + struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type); > > + if (IS_ERR(i2c_node)) > > + return PTR_ERR(i2c_node); > > I'm still getting comfortable with the __free() syntax so sorry if I'm > wrong, but I _think_ the above is buggy. I believe that the definition > of the free function for "device_node" is from: > > DEFINE_FREE(device_node, struct device_node *, if (_T) of_node_put(_T)) > > ...which means it'll call of_node_put() to free "i2c_node" when it > goes out of scope. of_node_put() handles NULL pointers but _not_ ERR > pointers. So I think that if you get an error back and then return via > the PTR_ERR(i2c_node) then it'll crash because it will try to free an > ERR pointer. Did I get that right? Presumably you need to instead do: > > return PTR_ERR(no_free_ptr(i2c_node)); > > ...or change of_node_put() to be a noop for error pointers? I could state that device_node FREE class has to be updated for these cases. fwnode, for example, handles both error pointers and NULL. -- With Best Regards, Andy Shevchenko
On Tue, Oct 08, 2024 at 03:34:23PM +0800, Chen-Yu Tsai wrote: > Some devices are designed and manufactured with some components having > multiple drop-in replacement options. These components are often > connected to the mainboard via ribbon cables, having the same signals > and pin assignments across all options. These may include the display > panel and touchscreen on laptops and tablets, and the trackpad on > laptops. Sometimes which component option is used in a particular device > can be detected by some firmware provided identifier, other times that > information is not available, and the kernel has to try to probe each > device. > > This change attempts to make the "probe each device" case cleaner. The > current approach is to have all options added and enabled in the device > tree. The kernel would then bind each device and run each driver's probe > function. This works, but has been broken before due to the introduction > of asynchronous probing, causing multiple instances requesting "shared" > resources, such as pinmuxes, GPIO pins, interrupt lines, at the same > time, with only one instance succeeding. Work arounds for these include > moving the pinmux to the parent I2C controller, using GPIO hogs or > pinmux settings to keep the GPIO pins in some fixed configuration, and > requesting the interrupt line very late. Such configurations can be seen > on the MT8183 Krane Chromebook tablets, and the Qualcomm sc8280xp-based > Lenovo Thinkpad 13S. > > Instead of this delicate dance between drivers and device tree quirks, > this change introduces a simple I2C component probe function. For a > given class of devices on the same I2C bus, it will go through all of > them, doing a simple I2C read transfer and see which one of them responds. > It will then enable the device that responds. > > This requires some minor modifications in the existing device tree. The > status for all the device nodes for the component options must be set > to "fail-needs-probe". This makes it clear that some mechanism is > needed to enable one of them, and also prevents the prober and device > drivers running at the same time. Fresh reading of the commit message make me think why the firmware or bootloader on such a device can't form a dynamic OF (overlay?) to fulfill the need? Another question is that we have the autoprobing mechanism for I2C for ages, why that one can't be (re-)used / extended to cover these cases? ... > +#ifndef _LINUX_I2C_OF_PROBER_H > +#define _LINUX_I2C_OF_PROBER_H Missing kconfig.h. > +struct device; > +struct device_node; > + > +/** > + * struct i2c_of_probe_ops - I2C OF component prober callbacks > + * > + * A set of callbacks to be used by i2c_of_probe_component(). > + * > + * All callbacks are optional. Callbacks are called only once per run, and are > + * used in the order they are defined in this structure. > + * > + * All callbacks that have return values shall return %0 on success, > + * or a negative error number on failure. > + * > + * The @dev parameter passed to the callbacks is the same as @dev passed to > + * i2c_of_probe_component(). It should only be used for dev_printk() calls > + * and nothing else, especially not managed device resource (devres) APIs. > + */ > +struct i2c_of_probe_ops { > + /** > + * @enable: Retrieve and enable resources so that the components respond to probes. > + * > + * Resources should be reverted to their initial state before returning if this fails. > + */ > + int (*enable)(struct device *dev, struct device_node *bus_node, void *data); > + > + /** > + * @cleanup_early: Release exclusive resources prior to enabling component. > + * > + * Only called if a matching component is actually found. If none are found, > + * resources that would have been released in this callback should be released in > + * @free_resourcs_late instead. > + */ > + void (*cleanup_early)(struct device *dev, void *data); > + > + /** > + * @cleanup: Opposite of @enable to balance refcounts and free resources after probing. > + * > + * Should check if resources were already freed by @cleanup_early. > + */ > + void (*cleanup)(struct device *dev, void *data); > +}; > + > +/** > + * struct i2c_of_probe_cfg - I2C OF component prober configuration > + * @ops: Callbacks for the prober to use. > + * @type: A string to match the device node name prefix to probe for. > + */ > +struct i2c_of_probe_cfg { > + const struct i2c_of_probe_ops *ops; > + const char *type; > +}; > + > +#if IS_ENABLED(CONFIG_OF_DYNAMIC) > + > +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx); > + > +#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ > + > +#endif /* _LINUX_I2C_OF_PROBER_H */ -- With Best Regards, Andy Shevchenko
On Thu, Oct 10, 2024 at 11:16 PM Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote: > > On Tue, Oct 08, 2024 at 03:34:23PM +0800, Chen-Yu Tsai wrote: > > Some devices are designed and manufactured with some components having > > multiple drop-in replacement options. These components are often > > connected to the mainboard via ribbon cables, having the same signals > > and pin assignments across all options. These may include the display > > panel and touchscreen on laptops and tablets, and the trackpad on > > laptops. Sometimes which component option is used in a particular device > > can be detected by some firmware provided identifier, other times that > > information is not available, and the kernel has to try to probe each > > device. > > > > This change attempts to make the "probe each device" case cleaner. The > > current approach is to have all options added and enabled in the device > > tree. The kernel would then bind each device and run each driver's probe > > function. This works, but has been broken before due to the introduction > > of asynchronous probing, causing multiple instances requesting "shared" > > resources, such as pinmuxes, GPIO pins, interrupt lines, at the same > > time, with only one instance succeeding. Work arounds for these include > > moving the pinmux to the parent I2C controller, using GPIO hogs or > > pinmux settings to keep the GPIO pins in some fixed configuration, and > > requesting the interrupt line very late. Such configurations can be seen > > on the MT8183 Krane Chromebook tablets, and the Qualcomm sc8280xp-based > > Lenovo Thinkpad 13S. > > > > Instead of this delicate dance between drivers and device tree quirks, > > this change introduces a simple I2C component probe function. For a > > given class of devices on the same I2C bus, it will go through all of > > them, doing a simple I2C read transfer and see which one of them responds. > > It will then enable the device that responds. > > > > This requires some minor modifications in the existing device tree. The > > status for all the device nodes for the component options must be set > > to "fail-needs-probe". This makes it clear that some mechanism is > > needed to enable one of them, and also prevents the prober and device > > drivers running at the same time. > > Fresh reading of the commit message make me think why the firmware or > bootloader on such a device can't form a dynamic OF (overlay?) to fulfill > the need? The firmware / bootloader on existing devices are practically not upgradable. On the other hand, the kernel is very easy to upgrade or swap out. For said shipped devices, there is also nothing to key the detection off of besides actually powering things up and doing I2C transfers, which takes time that the firmware has little to spare. We (ChromeOS) require that the bootloader jump into the kernel within 1 second of power on. That includes DRAM calibration, whatever essential hardware initialization, and loading and uncompressing the kernel. Anything non-essential that can be done in the kernel is going to get deferred to the kernel. Also, due to project timelines oftentimes the devices are shipped with a downstream kernel with downstream device trees. We don't want to tie the firmware too tightly to the device tree in case the downstream stuff gets reworked when upstreamed. > Another question is that we have the autoprobing mechanism for I2C for ages, > why that one can't be (re-)used / extended to cover these cases? I haven't looked into it very much, but a quick read of Documentation/i2c/instantiating-devices.rst suggests that it's solving a different problem? In our case, we know that it is just one of a handful of possible devices that we already described in the device tree. We don't need to probe the full address range nor the full range of drivers. We already have a hacky workaround in place, but that mangles the device tree in a way that doesn't really match the hardware. The components that we are handling don't seem to have any hardware ID register, nor do their drivers implement the .detect() callback. There's also power sequencing (regulator and GPIO lines) and interrupt lines from the device tree that need to be handled, something that is missing in the autoprobe path. Based on the above I don't think the existing autoprobe is a good fit. Trying to shoehorn it in is likely going to be a mess. Doug's original cover letter describes the problem in more detail, including why we think this should be done in the kernel, not the firmware: https://lore.kernel.org/all/20230921102420.RFC.1.I9dddd99ccdca175e3ceb1b9fa1827df0928c5101@changeid/ > ... > > > +#ifndef _LINUX_I2C_OF_PROBER_H > > +#define _LINUX_I2C_OF_PROBER_H > > Missing kconfig.h. Ack. Thanks ChenYu > > +struct device; > > +struct device_node; > > + > > +/** > > + * struct i2c_of_probe_ops - I2C OF component prober callbacks > > + * > > + * A set of callbacks to be used by i2c_of_probe_component(). > > + * > > + * All callbacks are optional. Callbacks are called only once per run, and are > > + * used in the order they are defined in this structure. > > + * > > + * All callbacks that have return values shall return %0 on success, > > + * or a negative error number on failure. > > + * > > + * The @dev parameter passed to the callbacks is the same as @dev passed to > > + * i2c_of_probe_component(). It should only be used for dev_printk() calls > > + * and nothing else, especially not managed device resource (devres) APIs. > > + */ > > +struct i2c_of_probe_ops { > > + /** > > + * @enable: Retrieve and enable resources so that the components respond to probes. > > + * > > + * Resources should be reverted to their initial state before returning if this fails. > > + */ > > + int (*enable)(struct device *dev, struct device_node *bus_node, void *data); > > + > > + /** > > + * @cleanup_early: Release exclusive resources prior to enabling component. > > + * > > + * Only called if a matching component is actually found. If none are found, > > + * resources that would have been released in this callback should be released in > > + * @free_resourcs_late instead. > > + */ > > + void (*cleanup_early)(struct device *dev, void *data); > > + > > + /** > > + * @cleanup: Opposite of @enable to balance refcounts and free resources after probing. > > + * > > + * Should check if resources were already freed by @cleanup_early. > > + */ > > + void (*cleanup)(struct device *dev, void *data); > > +}; > > + > > +/** > > + * struct i2c_of_probe_cfg - I2C OF component prober configuration > > + * @ops: Callbacks for the prober to use. > > + * @type: A string to match the device node name prefix to probe for. > > + */ > > +struct i2c_of_probe_cfg { > > + const struct i2c_of_probe_ops *ops; > > + const char *type; > > +}; > > + > > +#if IS_ENABLED(CONFIG_OF_DYNAMIC) > > + > > +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx); > > + > > +#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ > > + > > +#endif /* _LINUX_I2C_OF_PROBER_H */ > > -- > With Best Regards, > Andy Shevchenko > >
On Mon, Oct 14, 2024 at 11:53:47AM +0800, Chen-Yu Tsai wrote: > On Thu, Oct 10, 2024 at 11:16 PM Andy Shevchenko > <andriy.shevchenko@linux.intel.com> wrote: > > On Tue, Oct 08, 2024 at 03:34:23PM +0800, Chen-Yu Tsai wrote: ... > > Fresh reading of the commit message make me think why the firmware or > > bootloader on such a device can't form a dynamic OF (overlay?) to fulfill > > the need? > > The firmware / bootloader on existing devices are practically not upgradable. > On the other hand, the kernel is very easy to upgrade or swap out. > > For said shipped devices, there is also nothing to key the detection > off of besides actually powering things up and doing I2C transfers, > which takes time that the firmware has little to spare. We (ChromeOS) > require that the bootloader jump into the kernel within 1 second of > power on. That includes DRAM calibration, whatever essential hardware > initialization, and loading and uncompressing the kernel. Anything > non-essential that can be done in the kernel is going to get deferred > to the kernel. > > Also, due to project timelines oftentimes the devices are shipped with a > downstream kernel with downstream device trees. We don't want to tie the > firmware too tightly to the device tree in case the downstream stuff gets > reworked when upstreamed. Okay, I was always under impression that DT has at least one nice feature in comparison with ACPI that it can be replaced / updated in much quicker / independent manner. What you are telling seems like the same issue that ACPI-based platforms have. However, there they usually put all possible devices into DSDT and firmware enables them via run-time (ACPI) variables. Are you trying to implement something similar here? ... > > Another question is that we have the autoprobing mechanism for I2C for ages, > > why that one can't be (re-)used / extended to cover these cases? > > I haven't looked into it very much, but a quick read of > Documentation/i2c/instantiating-devices.rst suggests that it's solving > a different problem? > > In our case, we know that it is just one of a handful of possible > devices that we already described in the device tree. We don't need > to probe the full address range nor the full range of drivers. We > already have a hacky workaround in place, but that mangles the > device tree in a way that doesn't really match the hardware. > > The components that we are handling don't seem to have any hardware > ID register, nor do their drivers implement the .detect() callback. > There's also power sequencing (regulator and GPIO lines) and interrupt > lines from the device tree that need to be handled, something that is > missing in the autoprobe path. > > Based on the above I don't think the existing autoprobe is a good fit. > Trying to shoehorn it in is likely going to be a mess. > > Doug's original cover letter describes the problem in more detail, > including why we think this should be done in the kernel, not the > firmware: > https://lore.kernel.org/all/20230921102420.RFC.1.I9dddd99ccdca175e3ceb1b9fa1827df0928c5101@changeid/ Perhaps it needs to be summarised to cover at least this question along with the above? -- With Best Regards, Andy Shevchenko
On Mon, Oct 14, 2024 at 7:16 PM Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote: > > On Mon, Oct 14, 2024 at 11:53:47AM +0800, Chen-Yu Tsai wrote: > > On Thu, Oct 10, 2024 at 11:16 PM Andy Shevchenko > > <andriy.shevchenko@linux.intel.com> wrote: > > > On Tue, Oct 08, 2024 at 03:34:23PM +0800, Chen-Yu Tsai wrote: > > ... > > > > Fresh reading of the commit message make me think why the firmware or > > > bootloader on such a device can't form a dynamic OF (overlay?) to fulfill > > > the need? > > > > The firmware / bootloader on existing devices are practically not upgradable. > > On the other hand, the kernel is very easy to upgrade or swap out. > > > > For said shipped devices, there is also nothing to key the detection > > off of besides actually powering things up and doing I2C transfers, > > which takes time that the firmware has little to spare. We (ChromeOS) > > require that the bootloader jump into the kernel within 1 second of > > power on. That includes DRAM calibration, whatever essential hardware > > initialization, and loading and uncompressing the kernel. Anything > > non-essential that can be done in the kernel is going to get deferred > > to the kernel. > > > > Also, due to project timelines oftentimes the devices are shipped with a > > downstream kernel with downstream device trees. We don't want to tie the > > firmware too tightly to the device tree in case the downstream stuff gets > > reworked when upstreamed. > > Okay, I was always under impression that DT has at least one nice feature in > comparison with ACPI that it can be replaced / updated in much quicker / > independent manner. What you are telling seems like the same issue that That depends on which camp one is in. Some folks advocate for shipping the DT with the firmware. In that case you are limited by how easy the firmware is to upgrade. Or they would leave an option for the firmware to load a newer DT from disk. The other camp is shipping the DT with the kernel image, which ChromeOS and some Linux distros do. Then the DT is as easy to upgrade as the kernel. Opponents would argue that the DT is a hardware description and should be separate from the kernel. However as with all hardware bringup development, one can't really describe something that hasn't been developed and gone through review. However here I am specifically talking about the firmware _code_ being tied to the DT. To implement the probing feature in firmware, one needs to either add a lot more code about what devices the system might have, or implement the equivalent of this series in firmware, or something in between. This is a lot of code that depends on the structure of the DT it was developed against, which most likely was downstream at that point. Say someone screwed up some DT structure downstream at the time of release, such as the node name or address prefix, and that was fixed upstream. The new upstream DT no longer has the structure the firmware is expecting, and the firmware might not be able to handle it anymore, resulting in some peripheral no longer getting probed or enabled. And you don't have a way to upgrade the firmware to fix this. > ACPI-based platforms have. However, there they usually put all possible devices > into DSDT and firmware enables them via run-time (ACPI) variables. Are you > trying to implement something similar here? Yes, that sounds similar. However for us the DT is tied to the kernel, not the firmware, i.e. not provided by the firmware. There's also no UEFI in our case. > ... > > > > Another question is that we have the autoprobing mechanism for I2C for ages, > > > why that one can't be (re-)used / extended to cover these cases? > > > > I haven't looked into it very much, but a quick read of > > Documentation/i2c/instantiating-devices.rst suggests that it's solving > > a different problem? > > > > In our case, we know that it is just one of a handful of possible > > devices that we already described in the device tree. We don't need > > to probe the full address range nor the full range of drivers. We > > already have a hacky workaround in place, but that mangles the > > device tree in a way that doesn't really match the hardware. > > > > The components that we are handling don't seem to have any hardware > > ID register, nor do their drivers implement the .detect() callback. > > There's also power sequencing (regulator and GPIO lines) and interrupt > > lines from the device tree that need to be handled, something that is > > missing in the autoprobe path. > > > > Based on the above I don't think the existing autoprobe is a good fit. > > Trying to shoehorn it in is likely going to be a mess. > > > > Doug's original cover letter describes the problem in more detail, > > including why we think this should be done in the kernel, not the > > firmware: > > https://lore.kernel.org/all/20230921102420.RFC.1.I9dddd99ccdca175e3ceb1b9fa1827df0928c5101@changeid/ > > Perhaps it needs to be summarised to cover at least this question along with > the above? For the first question, it boils down to we think that the firmware should be simple and do as little as possible. It also should not be tied to a specific DT, so editing or fixing up the DT in firmware is something we avoid. The firmware can do overlays if they are provided, however in this particular case, the identifiers used by the bootloader don't cover the variations of the I2C-based second source components. For why autoprobing isn't a good fit, I believe my answer above covers it. ChenYu
© 2016 - 2024 Red Hat, Inc.