devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober
@ 2024-08-08  9:59 Chen-Yu Tsai
  2024-08-08  9:59 ` [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string Chen-Yu Tsai
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

Hi everyone,

This is v4 of my "of: Introduce hardware prober driver" [1] series.
v4 mainly adds regulator and GPIO support. This part was separately
tested on a MT8186 Voltorb device with a slightly device tree and
an extra entry in the ChromeOS hardware prober.

v2 continued Doug's "of: device: Support 2nd sources of probeable but
undiscoverable devices" [2] series, but follows the scheme suggested by
Rob, marking all second source component device nodes as "fail-needs-probe",
and having a hardware prober driver enable the one of them.

This time I trimmed down the Cc list.

Changes since v3:
- Patch 1 "of: dynamic: Add of_changeset_update_prop_string"
  - Use new __of_prop_free() helper (helper was added by Rob in 2024/04)
  - Add new line before header declaration
- Patch 2 "regulator: Add regulator_of_get_optional() for pure DT"
  - New patch
- Patch 3 "i2c: Introduce OF component probe function"
  - 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
- Patch 4 "i2c: of-prober: Add GPIO and regulator support"
  - New patch
- Patch 5 "platform/chrome: Introduce device tree hardware prober"
  - Include linux/init.h
  - Rewrite for loop in driver probe function as suggested by Andy
  - Make prober driver buildable as module
  - Ignore prober errors other than probe deferral
- Patch 6 "arm64: dts: mediatek: mt8173-elm-hana: Mark touchscreens and trackpads as fail"
  - Rebased

Patch "arm64: dts: mediatek: mt8173-elm-hana: Add G2touch G7500 touchscreen"
was merged separately and thus removed from this series.

Changes since v2:
- Added of_changeset_update_prop_string()
- Moved generic I2C code to the I2C core
- Moved remaining platform specific code to platform/chrome/
- Switched to of_node_is_available() to check if node is enabled.
- Switched to OF changeset API to update status property
- I2C probe helper function now accepts "struct device *dev" instead to
  reduce line length and dereferences
- Moved "ret = 0" to just before for_each_child_of_node(i2c_node, node)
- Depend on rather than select CONFIG_I2C
- Copied machine check to driver init function
- Explicitly mentioned "device tree" or OF in driver name, description
  and Kconfig symbol
- Dropped filename from inside the file
- Made loop variable size_t (instead of unsigned int as Andy asked)
- Switched to PLATFORM_DEVID_NONE instead of raw -1
- Switched to standard goto error path pattern in hw_prober_driver_init()
- Dropped device class from status property

Patches removed from v3 and saved for later:
- of: base: Add of_device_is_fail
- of: hw_prober: Support Chromebook SKU ID based component selection
- dt-bindings: arm: mediatek: Remove SKU specific compatibles for Google Krane
- arm64: dts: mediatek: mt8183-kukui: Merge Krane device trees

For the I2C component (touchscreens and trackpads) case from the
original series, the hardware prober driver finds the particular
class of device in the device tree, gets its parent I2C adapter,
and tries to initiate a simple I2C read for each device under that
I2C bus. When it finds one that responds, it considers that one
present, marks it as "okay", and returns, letting the driver core
actually probe the device.

This works fine in most cases since these components are connected
via a ribbon cable and always have the same resources. The prober
will also grab these resources and enable them.

The other case, selecting a display panel to use based on the SKU ID
from the firmware, hit a bit of an issue with fixing the OF graph.
It has been left out since v3.

Patch 1 adds of_changeset_update_prop_string(), as requested by Rob.

Patch 2 adds a function for retrieving regulator supplies solely
using device tree nodes.

Patch 3 implements probing the I2C bus for presence of components as
a helper function in the I2C core.

Patch 4 implements regulator supply and GPIO support for the I2C
component prober.

Patch 5 adds a ChromeOS specific DT hardware prober. This initial
version targets the Hana Chromebooks, probing its I2C trackpads and
touchscreens.

Patch 6 modifies the Hana device tree and marks the touchscreens
and trackpads as "fail-needs-probe", ready for the driver to probe.


Assuming this is acceptable to folks, because there are compile
time dependencies, I think it would be easier for the code bits
(patches 1 through 4) to go through either the OF tree or I2C
tree. Patches 5 and 6 can go through the soc tree via the mediatek
tree.


Thanks
ChenYu

Chen-Yu Tsai (6):
  of: dynamic: Add of_changeset_update_prop_string
  regulator: Add regulator_of_get_optional() for pure DT regulator
    lookup
  i2c: Introduce OF component probe function
  i2c: of-prober: Add GPIO and regulator support
  platform/chrome: Introduce device tree hardware prober
  arm64: dts: mediatek: mt8173-elm-hana: Mark touchscreens and trackpads
    as fail

 .../boot/dts/mediatek/mt8173-elm-hana.dtsi    |  13 +
 arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi  |   4 +-
 drivers/i2c/Makefile                          |   1 +
 drivers/i2c/i2c-core-of-prober.c              | 425 ++++++++++++++++++
 drivers/of/dynamic.c                          |  44 ++
 drivers/platform/chrome/Kconfig               |  11 +
 drivers/platform/chrome/Makefile              |   1 +
 .../platform/chrome/chromeos_of_hw_prober.c   | 107 +++++
 drivers/regulator/core.c                      |  81 +++-
 drivers/regulator/devres.c                    |   2 +-
 drivers/regulator/internal.h                  |   2 +-
 include/linux/i2c.h                           |   4 +
 include/linux/of.h                            |   4 +
 include/linux/regulator/consumer.h            |   8 +
 14 files changed, 681 insertions(+), 26 deletions(-)
 create mode 100644 drivers/i2c/i2c-core-of-prober.c
 create mode 100644 drivers/platform/chrome/chromeos_of_hw_prober.c

-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string
  2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
@ 2024-08-08  9:59 ` Chen-Yu Tsai
  2024-08-13 11:11   ` Andy Shevchenko
  2024-08-08  9:59 ` [PATCH v4 2/6] regulator: Add regulator_of_get_optional() for pure DT regulator lookup Chen-Yu Tsai
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

Add a helper function to add string property updates to an OF changeset.
This is similar to of_changeset_add_prop_string(), but instead of adding
the property (and failing if it exists), it will update the property.

This shall be used later in the DT hardware prober.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---

Changes since v3:
- Use new __of_prop_free() helper
- Add new line before header declaration

Changes since v2:
- New patch added in v3
---
 drivers/of/dynamic.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of.h   |  4 ++++
 2 files changed, 48 insertions(+)

diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 110104a936d9..dfd1358d59d5 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -1072,3 +1072,47 @@ int of_changeset_add_prop_bool(struct of_changeset *ocs, struct device_node *np,
 	return of_changeset_add_prop_helper(ocs, np, &prop);
 }
 EXPORT_SYMBOL_GPL(of_changeset_add_prop_bool);
+
+static int of_changeset_update_prop_helper(struct of_changeset *ocs,
+					   struct device_node *np,
+					   const struct property *pp)
+{
+	struct property *new_pp;
+	int ret;
+
+	new_pp = __of_prop_dup(pp, GFP_KERNEL);
+	if (!new_pp)
+		return -ENOMEM;
+
+	ret = of_changeset_update_property(ocs, np, new_pp);
+	if (ret)
+		__of_prop_free(new_pp);
+
+	return ret;
+}
+
+/**
+ * of_changeset_update_prop_string - Add a string property update to a changeset
+ *
+ * @ocs:	changeset pointer
+ * @np:		device node pointer
+ * @prop_name:	name of the property to be updated
+ * @str:	pointer to null terminated string
+ *
+ * Create a string property to be updated and add it to a changeset.
+ *
+ * Return: 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_update_prop_string(struct of_changeset *ocs,
+				    struct device_node *np,
+				    const char *prop_name, const char *str)
+{
+	struct property prop;
+
+	prop.name = (char *)prop_name;
+	prop.length = strlen(str) + 1;
+	prop.value = (void *)str;
+
+	return of_changeset_update_prop_helper(ocs, np, &prop);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_prop_string);
diff --git a/include/linux/of.h b/include/linux/of.h
index 85b60ac9eec5..046283be1cd3 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -1651,6 +1651,10 @@ static inline int of_changeset_add_prop_u32(struct of_changeset *ocs,
 	return of_changeset_add_prop_u32_array(ocs, np, prop_name, &val, 1);
 }
 
+int of_changeset_update_prop_string(struct of_changeset *ocs,
+				    struct device_node *np,
+				    const char *prop_name, const char *str);
+
 int of_changeset_add_prop_bool(struct of_changeset *ocs, struct device_node *np,
 			       const char *prop_name);
 
-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 2/6] regulator: Add regulator_of_get_optional() for pure DT regulator lookup
  2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
  2024-08-08  9:59 ` [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string Chen-Yu Tsai
@ 2024-08-08  9:59 ` Chen-Yu Tsai
  2024-08-13 11:22   ` Andy Shevchenko
  2024-08-08  9:59 ` [PATCH v4 3/6] i2c: Introduce OF component probe function Chen-Yu Tsai
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

The to-be-introduced I2C component prober needs to enable regulator
supplies (and toggle GPIO pins) for the various components it intends
to probe. To support this, a new "pure DT lookup" method for getting
regulator supplies is needed, since the device normally requesting
the supply won't get created until after the component is probed to
be available.

This adds a new regulator_of_get_optional() for this purpose. The
underlying code that support the existing regulator_get*() functions
are extended to support this specific case.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v3:
- New patch
---
 drivers/regulator/core.c           | 81 ++++++++++++++++++++++--------
 drivers/regulator/devres.c         |  2 +-
 drivers/regulator/internal.h       |  2 +-
 include/linux/regulator/consumer.h |  8 +++
 4 files changed, 69 insertions(+), 24 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 7674b7f2df14..ba4542d76642 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -456,30 +456,29 @@ static struct device_node *of_get_child_regulator(struct device_node *parent,
 
 /**
  * of_get_regulator - get a regulator device node based on supply name
- * @dev: Device pointer for the consumer (of regulator) device
+ * @node: Device node pointer for supply property lookup
  * @supply: regulator supply name
  *
  * Extract the regulator device node corresponding to the supply name.
  * returns the device node corresponding to the regulator if found, else
  * returns NULL.
  */
-static struct device_node *of_get_regulator(struct device *dev, const char *supply)
+static struct device_node *of_get_regulator(struct device_node *node, const char *supply)
 {
 	struct device_node *regnode = NULL;
 	char prop_name[64]; /* 64 is max size of property name */
 
-	dev_dbg(dev, "Looking up %s-supply from device tree\n", supply);
+	pr_debug("%pOF: Looking up %s-supply from device tree\n", node, supply);
 
 	snprintf(prop_name, 64, "%s-supply", supply);
-	regnode = of_parse_phandle(dev->of_node, prop_name, 0);
+	regnode = of_parse_phandle(node, prop_name, 0);
 
 	if (!regnode) {
-		regnode = of_get_child_regulator(dev->of_node, prop_name);
+		regnode = of_get_child_regulator(node, prop_name);
 		if (regnode)
 			return regnode;
 
-		dev_dbg(dev, "Looking up %s property in node %pOF failed\n",
-				prop_name, dev->of_node);
+		pr_debug("%pOF: Looking up %s property failed\n", node, prop_name);
 		return NULL;
 	}
 	return regnode;
@@ -1996,8 +1995,14 @@ static struct regulator_dev *regulator_lookup_by_name(const char *name)
 /**
  * regulator_dev_lookup - lookup a regulator device.
  * @dev: device for regulator "consumer".
+ * @node: device node for regulator supply lookup.
+ *        Falls back to dev->of_node if NULL.
  * @supply: Supply name or regulator ID.
  *
+ * If dev is NULL and node is not NULL, a pure device tree lookup is assumed.
+ * That is, it will not use supply aliases and end after DT based lookup is
+ * done.
+ *
  * If successful, returns a struct regulator_dev that corresponds to the name
  * @supply and with the embedded struct device refcount incremented by one.
  * The refcount must be dropped by calling put_device().
@@ -2006,21 +2011,30 @@ static struct regulator_dev *regulator_lookup_by_name(const char *name)
  * in the future.
  */
 static struct regulator_dev *regulator_dev_lookup(struct device *dev,
+						  struct device_node *node,
 						  const char *supply)
 {
 	struct regulator_dev *r = NULL;
-	struct device_node *node;
+	struct device_node *regulator_node;
 	struct regulator_map *map;
 	const char *devname = NULL;
+	bool pure_dt_lookup = false;
+
+	pure_dt_lookup = (node && !dev);
 
-	regulator_supply_alias(&dev, &supply);
+	/* Pure DT lookup should use given supply name directly */
+	if (!pure_dt_lookup)
+		regulator_supply_alias(&dev, &supply);
+
+	if (!node && dev && dev->of_node)
+		node = dev->of_node;
 
 	/* first do a dt based lookup */
-	if (dev && dev->of_node) {
-		node = of_get_regulator(dev, supply);
-		if (node) {
-			r = of_find_regulator_by_node(node);
-			of_node_put(node);
+	if (node) {
+		regulator_node = of_get_regulator(node, supply);
+		if (regulator_node) {
+			r = of_find_regulator_by_node(regulator_node);
+			of_node_put(regulator_node);
 			if (r)
 				return r;
 
@@ -2032,6 +2046,10 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev,
 		}
 	}
 
+	/* Pure DT lookup stops here. */
+	if (pure_dt_lookup)
+		return ERR_PTR(-ENODEV);
+
 	/* if not found, try doing it non-dt way */
 	if (dev)
 		devname = dev_name(dev);
@@ -2076,7 +2094,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
 	if (rdev->supply)
 		return 0;
 
-	r = regulator_dev_lookup(dev, rdev->supply_name);
+	r = regulator_dev_lookup(dev, NULL, rdev->supply_name);
 	if (IS_ERR(r)) {
 		ret = PTR_ERR(r);
 
@@ -2169,8 +2187,8 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
 }
 
 /* Internal regulator request function */
-struct regulator *_regulator_get(struct device *dev, const char *id,
-				 enum regulator_get_type get_type)
+struct regulator *_regulator_get(struct device *dev, struct device_node *node,
+				 const char *id, enum regulator_get_type get_type)
 {
 	struct regulator_dev *rdev;
 	struct regulator *regulator;
@@ -2187,7 +2205,7 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
 		return ERR_PTR(-EINVAL);
 	}
 
-	rdev = regulator_dev_lookup(dev, id);
+	rdev = regulator_dev_lookup(dev, node, id);
 	if (IS_ERR(rdev)) {
 		ret = PTR_ERR(rdev);
 
@@ -2318,7 +2336,7 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
  */
 struct regulator *regulator_get(struct device *dev, const char *id)
 {
-	return _regulator_get(dev, id, NORMAL_GET);
+	return _regulator_get(dev, NULL, id, NORMAL_GET);
 }
 EXPORT_SYMBOL_GPL(regulator_get);
 
@@ -2345,7 +2363,7 @@ EXPORT_SYMBOL_GPL(regulator_get);
  */
 struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
 {
-	return _regulator_get(dev, id, EXCLUSIVE_GET);
+	return _regulator_get(dev, NULL, id, EXCLUSIVE_GET);
 }
 EXPORT_SYMBOL_GPL(regulator_get_exclusive);
 
@@ -2371,10 +2389,29 @@ EXPORT_SYMBOL_GPL(regulator_get_exclusive);
  */
 struct regulator *regulator_get_optional(struct device *dev, const char *id)
 {
-	return _regulator_get(dev, id, OPTIONAL_GET);
+	return _regulator_get(dev, NULL, id, OPTIONAL_GET);
 }
 EXPORT_SYMBOL_GPL(regulator_get_optional);
 
+/**
+ * regulator_of_get_optional - get optional regulator via device tree lookup
+ * @node: device node for regulator "consumer"
+ * @id: Supply name
+ *
+ * Returns a struct regulator corresponding to the regulator producer,
+ * or IS_ERR() condition containing errno.
+ *
+ * This is intended for use by consumers that want to get a regulator
+ * supply directly from a device node, and can and want to deal with
+ * absence of such supplies. This will _not_ consider supply aliases.
+ * See regulator_dev_lookup().
+ */
+struct regulator *regulator_of_get_optional(struct device_node *node, const char *id)
+{
+	return _regulator_get(NULL, node, id, OPTIONAL_GET);
+}
+EXPORT_SYMBOL_GPL(regulator_of_get_optional);
+
 static void destroy_regulator(struct regulator *regulator)
 {
 	struct regulator_dev *rdev = regulator->rdev;
@@ -4928,7 +4965,7 @@ int _regulator_bulk_get(struct device *dev, int num_consumers,
 		consumers[i].consumer = NULL;
 
 	for (i = 0; i < num_consumers; i++) {
-		consumers[i].consumer = _regulator_get(dev,
+		consumers[i].consumer = _regulator_get(dev, NULL,
 						       consumers[i].supply, get_type);
 		if (IS_ERR(consumers[i].consumer)) {
 			ret = dev_err_probe(dev, PTR_ERR(consumers[i].consumer),
diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 7111c46e9de1..7d9ea8232959 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -28,7 +28,7 @@ static struct regulator *_devm_regulator_get(struct device *dev, const char *id,
 	if (!ptr)
 		return ERR_PTR(-ENOMEM);
 
-	regulator = _regulator_get(dev, id, get_type);
+	regulator = _regulator_get(dev, NULL, id, get_type);
 	if (!IS_ERR(regulator)) {
 		*ptr = regulator;
 		devres_add(dev, ptr);
diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h
index 77a502141089..51eb552cba01 100644
--- a/drivers/regulator/internal.h
+++ b/drivers/regulator/internal.h
@@ -120,7 +120,7 @@ enum regulator_get_type {
 	MAX_GET_TYPE
 };
 
-struct regulator *_regulator_get(struct device *dev, const char *id,
+struct regulator *_regulator_get(struct device *dev, struct device_node *node, const char *id,
 				 enum regulator_get_type get_type);
 int _regulator_bulk_get(struct device *dev, int num_consumers,
 			struct regulator_bulk_data *consumers, enum regulator_get_type get_type);
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index d986ec13092e..76826d0d99f1 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -168,6 +168,8 @@ int devm_regulator_get_enable_read_voltage(struct device *dev, const char *id);
 void regulator_put(struct regulator *regulator);
 void devm_regulator_put(struct regulator *regulator);
 
+struct regulator *__must_check regulator_of_get_optional(struct device_node *node, const char *id);
+
 int regulator_register_supply_alias(struct device *dev, const char *id,
 				    struct device *alias_dev,
 				    const char *alias_id);
@@ -358,6 +360,12 @@ static inline void devm_regulator_put(struct regulator *regulator)
 {
 }
 
+static inline struct regulator *__must_check
+regulator_of_get_optional(struct device_node *node, const char *id)
+{
+	return ERR_PTR(-ENODEV);
+}
+
 static inline void devm_regulator_bulk_put(struct regulator_bulk_data *consumers)
 {
 }
-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 3/6] i2c: Introduce OF component probe function
  2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
  2024-08-08  9:59 ` [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string Chen-Yu Tsai
  2024-08-08  9:59 ` [PATCH v4 2/6] regulator: Add regulator_of_get_optional() for pure DT regulator lookup Chen-Yu Tsai
@ 2024-08-08  9:59 ` Chen-Yu Tsai
  2024-08-13 11:26   ` Andy Shevchenko
  2024-08-08  9:59 ` [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support Chen-Yu Tsai
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

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 "failed-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 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)
---
 drivers/i2c/Makefile             |   1 +
 drivers/i2c/i2c-core-of-prober.c | 155 +++++++++++++++++++++++++++++++
 include/linux/i2c.h              |   4 +
 3 files changed, 160 insertions(+)
 create mode 100644 drivers/i2c/i2c-core-of-prober.c

diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 3f71ce4711e3..0c543c742895 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..08aa736cc7a9
--- /dev/null
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linux I2C core OF component prober code
+ *
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.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 and GPIOs.
+ * - Support I2C muxes
+ */
+
+/**
+ * i2c_of_probe_component() - probe for devices of "type" on the same i2c bus
+ * @dev: &struct device of the caller, only used for dev_* printk messages
+ * @type: a string to match the device node name prefix to probe for
+ *
+ * Probe for possible I2C components of the same "type" on the same I2C bus
+ * that have their status marked as "fail".
+ *
+ * Assumes that across the entire device tree the only instances of nodes
+ * prefixed with "type" 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 is 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". 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 char *type)
+{
+	struct device_node *node, *i2c_node;
+	struct i2c_adapter *i2c;
+	struct of_changeset *ocs = NULL;
+	int ret;
+
+	node = of_find_node_by_name(NULL, type);
+	if (!node)
+		return dev_err_probe(dev, -ENODEV, "Could not find %s device node\n", type);
+
+	i2c_node = of_get_next_parent(node);
+	if (!of_node_name_eq(i2c_node, "i2c")) {
+		of_node_put(i2c_node);
+		return dev_err_probe(dev, -EINVAL, "%s device isn't on I2C bus\n", type);
+	}
+
+	if (!of_device_is_available(i2c_node)) {
+		of_node_put(i2c_node);
+		return dev_err_probe(dev, -ENODEV, "I2C controller not available\n");
+	}
+
+	for_each_child_of_node(i2c_node, node) {
+		if (!of_node_name_prefix(node, type))
+			continue;
+		if (of_device_is_available(node)) {
+			/*
+			 * Device tree has component already enabled. Either the
+			 * device tree isn't supported or we already probed once.
+			 */
+			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(dev, -EPROBE_DEFER, "Couldn't get I2C adapter\n");
+	}
+
+	ret = 0;
+	for_each_child_of_node(i2c_node, node) {
+		union i2c_smbus_data data;
+		u32 addr;
+
+		if (!of_node_name_prefix(node, type))
+			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;
+
+		break;
+	}
+
+	/* Found a device that is responding */
+	if (node) {
+		dev_info(dev, "Enabling %pOF\n", node);
+
+		ocs = kzalloc(sizeof(*ocs), GFP_KERNEL);
+		if (!ocs) {
+			ret = -ENOMEM;
+			goto err_put_node;
+		}
+
+		of_changeset_init(ocs);
+		ret = of_changeset_update_prop_string(ocs, node, "status", "okay");
+		if (ret)
+			goto err_free_ocs;
+		ret = of_changeset_apply(ocs);
+		if (ret)
+			goto err_destroy_ocs;
+
+		of_node_put(node);
+	}
+
+	i2c_put_adapter(i2c);
+	of_node_put(i2c_node);
+
+	return 0;
+
+err_destroy_ocs:
+	of_changeset_destroy(ocs);
+err_free_ocs:
+	kfree(ocs);
+err_put_node:
+	of_node_put(node);
+	i2c_put_adapter(i2c);
+	of_node_put(i2c_node);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i2c_of_probe_component);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 7eedd0c662da..0415d5d0f6ac 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -1030,6 +1030,10 @@ const struct of_device_id
 int of_i2c_get_board_info(struct device *dev, struct device_node *node,
 			  struct i2c_board_info *info);
 
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+int i2c_of_probe_component(struct device *dev, const char *type);
+#endif
+
 #else
 
 static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support
  2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
                   ` (2 preceding siblings ...)
  2024-08-08  9:59 ` [PATCH v4 3/6] i2c: Introduce OF component probe function Chen-Yu Tsai
@ 2024-08-08  9:59 ` Chen-Yu Tsai
  2024-08-13 11:41   ` Andy Shevchenko
  2024-08-08  9:59 ` [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober Chen-Yu Tsai
  2024-08-08  9:59 ` [PATCH v4 6/6] arm64: dts: mediatek: mt8173-elm-hana: Mark touchscreens and trackpads as fail Chen-Yu Tsai
  5 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

This adds GPIO and regulator management to the I2C OF component prober.
Components that the prober intends to probe likely require their
regulator supplies be enabled, and GPIOs be toggled to enable them or
bring them out of reset before they will respond to probe attempts.

Without specific knowledge of each component's resource names or
power sequencing requirements, the prober can only enable the
regulator supplies all at once, and toggle the GPIOs all at once.
Luckily, reset pins tend to be active low, while enable pins tend to
be active high, so setting the raw status of all GPIO pins to high
should work. The wait time before and after resources are enabled
are collected from existing drivers and device trees.

The prober collects resources from all possible components and enables
them together, instead of enabling resources and probing each component
one by one. The latter approach does not provide any boot time benefits
over simply enabling each component and letting each driver probe
sequentially.

The prober will also deduplicate the resources, since on a component
swap out or co-layout design, the resources are always the same.
While duplicate regulator supplies won't cause much issue, shared
GPIOs don't work reliably, especially with other drivers. For the
same reason, the prober will release the GPIOs before the successfully
probed component is actually enabled.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v3:
- New patch

This change is kept as a separate patch for now since the changes are
quite numerous.
---
 drivers/i2c/i2c-core-of-prober.c | 272 ++++++++++++++++++++++++++++++-
 1 file changed, 271 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
index 08aa736cc7a9..76d26768e12c 100644
--- a/drivers/i2c/i2c-core-of-prober.c
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -5,11 +5,14 @@
  * Copyright (C) 2024 Google LLC
  */
 
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
 /*
@@ -25,10 +28,242 @@
  * address responds.
  *
  * TODO:
- * - Support handling common regulators and GPIOs.
  * - Support I2C muxes
  */
 
+/*
+ * While 8 seems like a small number, especially when probing many component
+ * options, in practice all the options will have the same resources. The
+ * code getting the resources below does deduplication to avoid conflicts.
+ */
+#define RESOURCE_MAX 8
+
+struct i2c_of_probe_data {
+	struct of_phandle_args gpio_phandles[RESOURCE_MAX];
+	unsigned int gpio_phandles_num;
+	struct gpio_desc *gpiods[RESOURCE_MAX];
+	unsigned int gpiods_num;
+	struct regulator *regulators[RESOURCE_MAX];
+	unsigned int regulators_num;
+};
+
+#define REGULATOR_SUFFIX "-supply"
+
+/* Returns 1 if regulator found for property, 0 if not, or error. */
+static int i2c_of_probe_get_regulator(struct device_node *node, struct property *prop,
+				      struct i2c_of_probe_data *data)
+{
+	struct regulator *regulator = NULL;
+	char con[32]; /* 32 is max size of property name */
+	char *p;
+
+	p = strstr(prop->name, REGULATOR_SUFFIX);
+	if (!p)
+		return 0;
+
+	if (strcmp(p, REGULATOR_SUFFIX))
+		return 0;
+
+	strscpy(con, prop->name, p - prop->name + 1);
+	regulator = regulator_of_get_optional(node, con);
+	/* DT lookup should never return -ENODEV */
+	if (IS_ERR(regulator))
+		return PTR_ERR(regulator);
+
+	for (int i = 0; i < data->regulators_num; i++)
+		if (regulator_is_equal(regulator, data->regulators[i])) {
+			regulator_put(regulator);
+			regulator = NULL;
+			break;
+		}
+
+	if (!regulator)
+		return 1;
+
+	if (data->regulators_num == ARRAY_SIZE(data->regulators)) {
+		regulator_put(regulator);
+		return -ENOMEM;
+	}
+
+	data->regulators[data->regulators_num++] = regulator;
+
+	return 1;
+}
+
+#define GPIO_SUFFIX "-gpio"
+
+/* Returns 1 if GPIO found for property, 0 if not, or error. */
+static int i2c_of_probe_get_gpiod(struct device_node *node, struct property *prop,
+				  struct i2c_of_probe_data *data)
+{
+	struct fwnode_handle *fwnode = of_fwnode_handle(node);
+	struct gpio_desc *gpiod = NULL;
+	char con[32]; /* 32 is max size of property name */
+	char *con_id = NULL;
+	char *p;
+	struct of_phandle_args phargs;
+	int ret;
+	bool duplicate_found;
+
+	p = strstr(prop->name, GPIO_SUFFIX);
+	if (p) {
+		strscpy(con, prop->name, p - prop->name + 1);
+		con_id = con;
+	} else if (strcmp(prop->name, "gpio") && strcmp(prop->name, "gpios")) {
+		return 0;
+	}
+
+	ret = of_parse_phandle_with_args_map(node, prop->name, "gpio", 0, &phargs);
+	if (ret)
+		return ret;
+
+	/*
+	 * GPIO descriptors are not reference counted. GPIOD_FLAGS_BIT_NONEXCLUSIVE
+	 * can't differentiate between GPIOs shared between devices to be probed and
+	 * other devices (which is incorrect). Instead we check the parsed phandle
+	 * for duplicates. Ignore the flags (the last arg) in this case.
+	 */
+	phargs.args[phargs.args_count - 1] = 0;
+	duplicate_found = false;
+	for (int i = 0; i < data->gpio_phandles_num; i++)
+		if (of_phandle_args_equal(&phargs, &data->gpio_phandles[i])) {
+			duplicate_found = true;
+			break;
+		}
+
+	if (duplicate_found) {
+		of_node_put(phargs.np);
+		return 1;
+	}
+
+	gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
+	if (IS_ERR(gpiod)) {
+		of_node_put(phargs.np);
+		return PTR_ERR(gpiod);
+	}
+
+	if (data->gpiods_num == ARRAY_SIZE(data->gpiods)) {
+		of_node_put(phargs.np);
+		gpiod_put(gpiod);
+		return -ENOMEM;
+	}
+
+	memcpy(&data->gpio_phandles[data->gpio_phandles_num++], &phargs, sizeof(phargs));
+	data->gpiods[data->gpiods_num++] = gpiod;
+
+	return 1;
+}
+
+/*
+ * This is split into two functions because in the normal flow the GPIOs
+ * have to be released before the actual driver probes so that the latter
+ * can acquire them.
+ */
+static void i2c_of_probe_free_gpios(struct i2c_of_probe_data *data)
+{
+	for (int i = data->gpio_phandles_num - 1; i >= 0; i--)
+		of_node_put(data->gpio_phandles[i].np);
+	data->gpio_phandles_num = 0;
+
+	for (int i = data->gpiods_num - 1; i >= 0; i--)
+		gpiod_put(data->gpiods[i]);
+	data->gpiods_num = 0;
+}
+
+static void i2c_of_probe_free_res(struct i2c_of_probe_data *data)
+{
+	i2c_of_probe_free_gpios(data);
+
+	for (int i = data->regulators_num; i >= 0; i--)
+		regulator_put(data->regulators[i]);
+	data->regulators_num = 0;
+}
+
+static int i2c_of_probe_get_res(struct device *dev, struct device_node *node,
+				struct i2c_of_probe_data *data)
+{
+	struct property *prop;
+	int ret;
+
+	for_each_property_of_node(node, prop) {
+		dev_dbg(dev, "Trying property %pOF/%s\n", node, prop->name);
+
+		/* regulator supplies */
+		ret = i2c_of_probe_get_regulator(node, prop, data);
+		if (ret > 0)
+			continue;
+		if (ret < 0) {
+			dev_err_probe(dev, ret, "Failed to get regulator supply from %pOF/%s\n",
+				      node, prop->name);
+			goto err_cleanup;
+		}
+
+		/* GPIOs */
+		ret = i2c_of_probe_get_gpiod(node, prop, data);
+		if (ret < 0) {
+			dev_err_probe(dev, ret, "Failed to get GPIO from %pOF/%s\n",
+				      node, prop->name);
+			goto err_cleanup;
+		}
+	}
+
+	return 0;
+
+err_cleanup:
+	i2c_of_probe_free_res(data);
+	return ret;
+}
+
+static int i2c_of_probe_enable_res(struct device *dev, struct i2c_of_probe_data *data)
+{
+	int ret = 0;
+	int reg_i, gpio_i;
+
+	dev_dbg(dev, "Enabling resources\n");
+
+	for (reg_i = 0; reg_i < data->regulators_num; reg_i++) {
+		dev_dbg(dev, "Enabling regulator %d\n", reg_i);
+		ret = regulator_enable(data->regulators[reg_i]);
+		if (ret)
+			goto disable_regulators;
+	}
+
+	/* largest post-power-on pre-reset-deassert delay seen among drivers */
+	msleep(500);
+
+	for (gpio_i = 0; gpio_i < data->gpiods_num; gpio_i++) {
+		/*
+		 * reset GPIOs normally have opposite polarity compared to
+		 * enable GPIOs. Instead of parsing the flags again, simply
+		 * set the raw value to high.
+		 */
+		dev_dbg(dev, "Setting GPIO %d\n", gpio_i);
+		ret = gpiod_direction_output_raw(data->gpiods[gpio_i], 1);
+		if (ret)
+			goto disable_gpios;
+	}
+
+	/* largest post-reset-deassert delay seen in tree for Elan I2C HID */
+	msleep(300);
+
+	return 0;
+
+disable_gpios:
+	for (gpio_i--; gpio_i >= 0; gpio_i--)
+		gpiod_set_raw_value_cansleep(data->gpiods[gpio_i], 0);
+disable_regulators:
+	for (reg_i--; reg_i >= 0; reg_i--)
+		regulator_disable(data->regulators[reg_i]);
+
+	return ret;
+}
+
+static void i2c_of_probe_disable_regulators(struct i2c_of_probe_data *data)
+{
+	for (int i = data->regulators_num - 1; i >= 0; i--)
+		regulator_disable(data->regulators[i]);
+}
+
 /**
  * i2c_of_probe_component() - probe for devices of "type" on the same i2c bus
  * @dev: &struct device of the caller, only used for dev_* printk messages
@@ -64,6 +299,7 @@ int i2c_of_probe_component(struct device *dev, const char *type)
 	struct device_node *node, *i2c_node;
 	struct i2c_adapter *i2c;
 	struct of_changeset *ocs = NULL;
+	struct i2c_of_probe_data data = {0};
 	int ret;
 
 	node = of_find_node_by_name(NULL, type);
@@ -101,6 +337,34 @@ int i2c_of_probe_component(struct device *dev, const char *type)
 		return dev_err_probe(dev, -EPROBE_DEFER, "Couldn't get I2C adapter\n");
 	}
 
+	/* Grab resources */
+	for_each_child_of_node_scoped(i2c_node, node) {
+		u32 addr;
+
+		if (!of_node_name_prefix(node, type))
+			continue;
+		if (of_property_read_u32(node, "reg", &addr))
+			continue;
+
+		dev_dbg(dev, "Requesting resources for %pOF\n", node);
+		ret = i2c_of_probe_get_res(dev, node, &data);
+		if (ret) {
+			of_node_put(i2c_node);
+			return ret;
+		}
+	}
+
+	dev_dbg(dev, "Resources: # of GPIOs = %d, # of regulator supplies = %d\n",
+		data.gpiods_num, data.regulators_num);
+
+	/* Enable resources */
+	ret = i2c_of_probe_enable_res(dev, &data);
+	if (ret) {
+		i2c_of_probe_free_res(&data);
+		of_node_put(i2c_node);
+		return dev_err_probe(dev, ret, "Failed to enable resources\n");
+	}
+
 	ret = 0;
 	for_each_child_of_node(i2c_node, node) {
 		union i2c_smbus_data data;
@@ -116,6 +380,8 @@ int i2c_of_probe_component(struct device *dev, const char *type)
 		break;
 	}
 
+	i2c_of_probe_free_gpios(&data);
+
 	/* Found a device that is responding */
 	if (node) {
 		dev_info(dev, "Enabling %pOF\n", node);
@@ -137,6 +403,8 @@ int i2c_of_probe_component(struct device *dev, const char *type)
 		of_node_put(node);
 	}
 
+	i2c_of_probe_disable_regulators(&data);
+	i2c_of_probe_free_res(&data);
 	i2c_put_adapter(i2c);
 	of_node_put(i2c_node);
 
@@ -148,6 +416,8 @@ int i2c_of_probe_component(struct device *dev, const char *type)
 	kfree(ocs);
 err_put_node:
 	of_node_put(node);
+	i2c_of_probe_disable_regulators(&data);
+	i2c_of_probe_free_res(&data);
 	i2c_put_adapter(i2c);
 	of_node_put(i2c_node);
 	return ret;
-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober
  2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
                   ` (3 preceding siblings ...)
  2024-08-08  9:59 ` [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support Chen-Yu Tsai
@ 2024-08-08  9:59 ` Chen-Yu Tsai
  2024-08-13 11:46   ` Andy Shevchenko
  2024-08-08  9:59 ` [PATCH v4 6/6] arm64: dts: mediatek: mt8173-elm-hana: Mark touchscreens and trackpads as fail Chen-Yu Tsai
  5 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

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 prober. For any 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 "failed-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 v3:
- Include linux/init.h
- Rewrite for loop in driver probe function as suggested by Andy
- Make prober driver buildable as module
- Ignore prober errors other than probe deferral

Changes since v2:
- Addressed Rob's comments
  - Move remaining driver code to drivers/platform/chrome/
  - Depend on rather than select CONFIG_I2C
  - Copy machine check to driver init function
- Addressed Andy's comments
  - Explicitly mention "device tree" or OF in driver name, description
    and Kconfig symbol
  - Drop filename from inside the file
  - Switch to passing "struct device *" to shorten lines
  - Move "ret = 0" to just before for_each_child_of_node(i2c_node, node)
  - Make loop variable size_t (instead of unsigned int as Andy asked)
  - Use PLATFORM_DEVID_NONE instead of raw -1
  - Use standard goto error path pattern in hw_prober_driver_init()

- Changes since v1:
  - New patch
---
 drivers/platform/chrome/Kconfig               |  11 ++
 drivers/platform/chrome/Makefile              |   1 +
 .../platform/chrome/chromeos_of_hw_prober.c   | 107 ++++++++++++++++++
 3 files changed, 119 insertions(+)
 create mode 100644 drivers/platform/chrome/chromeos_of_hw_prober.c

diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 7dbeb786352a..acc740bd8bd9 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -61,6 +61,17 @@ config CHROMEOS_TBMC
 	  To compile this driver as a module, choose M here: the
 	  module will be called chromeos_tbmc.
 
+config CHROMEOS_OF_HW_PROBER
+	tristate "ChromeOS Device Tree Hardware Prober"
+	depends on OF
+	depends on I2C || !I2C # cannot be built-in for modular I2C
+	select OF_DYNAMIC
+	default OF
+	help
+	  This option enables the device tree hardware prober for ChromeOS
+	  devices. The driver will probe the correct component variant in
+	  devices that have multiple drop-in options for one component.
+
 config CROS_EC
 	tristate "ChromeOS Embedded Controller"
 	select CROS_EC_PROTO
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 2dcc6ccc2302..21a9d5047053 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CHROMEOS_ACPI)		+= chromeos_acpi.o
 obj-$(CONFIG_CHROMEOS_LAPTOP)		+= chromeos_laptop.o
 obj-$(CONFIG_CHROMEOS_PRIVACY_SCREEN)	+= chromeos_privacy_screen.o
 obj-$(CONFIG_CHROMEOS_PSTORE)		+= chromeos_pstore.o
+obj-$(CONFIG_CHROMEOS_OF_HW_PROBER)	+= chromeos_of_hw_prober.o
 obj-$(CONFIG_CHROMEOS_TBMC)		+= chromeos_tbmc.o
 obj-$(CONFIG_CROS_EC)			+= cros_ec.o
 obj-$(CONFIG_CROS_EC_I2C)		+= cros_ec_i2c.o
diff --git a/drivers/platform/chrome/chromeos_of_hw_prober.c b/drivers/platform/chrome/chromeos_of_hw_prober.c
new file mode 100644
index 000000000000..03bde52f9092
--- /dev/null
+++ b/drivers/platform/chrome/chromeos_of_hw_prober.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ChromeOS Device Tree Hardware Prober
+ *
+ * Copyright (c) 2023 Google LLC
+ */
+
+#include <linux/array_size.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define DRV_NAME	"chromeos_of_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 device *dev, const void *data);
+	const void *data;
+};
+
+static int chromeos_i2c_component_prober(struct device *dev, const void *data)
+{
+	const char *type = data;
+
+	return i2c_of_probe_component(dev, type);
+}
+
+static const struct hw_prober_entry hw_prober_platforms[] = {
+	{ .compatible = "google,hana", .prober = chromeos_i2c_component_prober, .data = "touchscreen" },
+	{ .compatible = "google,hana", .prober = chromeos_i2c_component_prober, .data = "trackpad" },
+};
+
+static int chromeos_of_hw_prober_probe(struct platform_device *pdev)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) {
+		if (!of_machine_is_compatible(hw_prober_platforms[i].compatible))
+			continue;
+
+		int ret;
+
+		ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data);
+		/* Ignore unrecoverable errors and keep going through other probers */
+		if (ret == -EPROBE_DEFER)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver chromeos_of_hw_prober_driver = {
+	.probe	= chromeos_of_hw_prober_probe,
+	.driver	= {
+		.name = DRV_NAME,
+	},
+};
+
+static struct platform_device *chromeos_of_hw_prober_pdev;
+
+static int chromeos_of_hw_prober_driver_init(void)
+{
+	size_t i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++)
+		if (of_machine_is_compatible(hw_prober_platforms[i].compatible))
+			break;
+	if (i == ARRAY_SIZE(hw_prober_platforms))
+		return 0;
+
+	ret = platform_driver_register(&chromeos_of_hw_prober_driver);
+	if (ret)
+		return ret;
+
+	chromeos_of_hw_prober_pdev =
+			platform_device_register_simple(DRV_NAME, PLATFORM_DEVID_NONE, NULL, 0);
+	if (IS_ERR(chromeos_of_hw_prober_pdev))
+		goto err;
+
+	return 0;
+
+err:
+	platform_driver_unregister(&chromeos_of_hw_prober_driver);
+
+	return PTR_ERR(chromeos_of_hw_prober_pdev);
+}
+module_init(chromeos_of_hw_prober_driver_init);
+
+static void chromeos_of_hw_prober_driver_exit(void)
+{
+	if (!chromeos_of_hw_prober_pdev)
+		return;
+
+	platform_device_unregister(chromeos_of_hw_prober_pdev);
+	platform_driver_unregister(&chromeos_of_hw_prober_driver);
+}
+module_exit(chromeos_of_hw_prober_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS device tree hardware prober");
-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 6/6] arm64: dts: mediatek: mt8173-elm-hana: Mark touchscreens and trackpads as fail
  2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
                   ` (4 preceding siblings ...)
  2024-08-08  9:59 ` [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober Chen-Yu Tsai
@ 2024-08-08  9:59 ` Chen-Yu Tsai
  5 siblings, 0 replies; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-08  9:59 UTC (permalink / raw)
  To: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood
  Cc: Chen-Yu Tsai, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, Andy Shevchenko, linux-i2c

Instead of having them all available, mark them all as "fail-needs-probe"
and have the implementation try to probe which one is present.

Also remove the shared resource workaround by moving the pinctrl entry
for the trackpad interrupt line back into the individual trackpad nodes.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v4:
- Rebased

Changes since v3:
- Also remove second source workaround, i.e. move the interrupt line
  pinctrl entry from the i2c node back to the components.

Changes since v2:
- Drop class from status
---
 arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi | 13 +++++++++++++
 arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi      |  4 ++--
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi
index 8d1cbc92bce3..251e084bf7de 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi
@@ -14,6 +14,7 @@ touchscreen2: touchscreen@34 {
 		compatible = "melfas,mip4_ts";
 		reg = <0x34>;
 		interrupts-extended = <&pio 88 IRQ_TYPE_LEVEL_LOW>;
+		status = "fail-needs-probe";
 	};
 
 	/*
@@ -26,6 +27,7 @@ touchscreen3: touchscreen@20 {
 		reg = <0x20>;
 		hid-descr-addr = <0x0020>;
 		interrupts-extended = <&pio 88 IRQ_TYPE_LEVEL_LOW>;
+		status = "fail-needs-probe";
 	};
 
 	/* Lenovo Ideapad C330 uses G2Touch touchscreen as a 2nd source touchscreen */
@@ -47,9 +49,12 @@ &i2c4 {
 	trackpad2: trackpad@2c {
 		compatible = "hid-over-i2c";
 		interrupts-extended = <&pio 117 IRQ_TYPE_LEVEL_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&trackpad_irq>;
 		reg = <0x2c>;
 		hid-descr-addr = <0x0020>;
 		wakeup-source;
+		status = "fail-needs-probe";
 	};
 };
 
@@ -74,3 +79,11 @@ pins_wp {
 		};
 	};
 };
+
+&touchscreen {
+	status = "fail-needs-probe";
+};
+
+&trackpad {
+	status = "fail-needs-probe";
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
index b4d85147b77b..eee64461421f 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
@@ -358,12 +358,12 @@ touchscreen: touchscreen@10 {
 &i2c4 {
 	clock-frequency = <400000>;
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&trackpad_irq>;
 
 	trackpad: trackpad@15 {
 		compatible = "elan,ekth3000";
 		interrupts-extended = <&pio 117 IRQ_TYPE_LEVEL_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&trackpad_irq>;
 		reg = <0x15>;
 		vcc-supply = <&mt6397_vgp6_reg>;
 		wakeup-source;
-- 
2.46.0.rc2.264.g509ed76dc8-goog


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string
  2024-08-08  9:59 ` [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string Chen-Yu Tsai
@ 2024-08-13 11:11   ` Andy Shevchenko
  2024-08-13 19:18     ` Rob Herring
  0 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-13 11:11 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Thu, Aug 08, 2024 at 05:59:24PM +0800, Chen-Yu Tsai wrote:
> Add a helper function to add string property updates to an OF changeset.
> This is similar to of_changeset_add_prop_string(), but instead of adding
> the property (and failing if it exists), it will update the property.
> 
> This shall be used later in the DT hardware prober.

...

> +int of_changeset_update_prop_string(struct of_changeset *ocs,
> +				    struct device_node *np,
> +				    const char *prop_name, const char *str)
> +{
> +	struct property prop;
> +
> +	prop.name = (char *)prop_name;
> +	prop.length = strlen(str) + 1;
> +	prop.value = (void *)str;

Is it the existing style in the file? Otherwise I often see style like this

	struct property prop = {
		.name = (char *)prop_name;
		.length = strlen(str) + 1;
		.value = (void *)str;
	};

in the kernel (IRQ domain, platform core, ...).

> +	return of_changeset_update_prop_helper(ocs, np, &prop);
> +}

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 2/6] regulator: Add regulator_of_get_optional() for pure DT regulator lookup
  2024-08-08  9:59 ` [PATCH v4 2/6] regulator: Add regulator_of_get_optional() for pure DT regulator lookup Chen-Yu Tsai
@ 2024-08-13 11:22   ` Andy Shevchenko
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-13 11:22 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Thu, Aug 08, 2024 at 05:59:25PM +0800, Chen-Yu Tsai wrote:
> The to-be-introduced I2C component prober needs to enable regulator
> supplies (and toggle GPIO pins) for the various components it intends
> to probe. To support this, a new "pure DT lookup" method for getting
> regulator supplies is needed, since the device normally requesting
> the supply won't get created until after the component is probed to
> be available.
> 
> This adds a new regulator_of_get_optional() for this purpose. The
> underlying code that support the existing regulator_get*() functions
> are extended to support this specific case.

...

>  /**
>   * regulator_dev_lookup - lookup a regulator device.
>   * @dev: device for regulator "consumer".
> + * @node: device node for regulator supply lookup.
> + *        Falls back to dev->of_node if NULL.

Please, avoid using dereferences in the comments. Use plain language:
"Falls back to the OF node of the @dev, if NULL." or alike.

>   * @supply: Supply name or regulator ID.

>   */

...

>  static struct regulator_dev *regulator_dev_lookup(struct device *dev,
> +						  struct device_node *node,

This function has no of_ prefix in its name. If you want to make it for all,
please use fwnode instead. Otherwise I would expect a new one with of_ prefix.
(But I really prefer just agnostic, i.e. fwnode, approach!)

>  						  const char *supply)
>  {
> +	bool pure_dt_lookup = false;

Redundant assignment.

> +	pure_dt_lookup = (node && !dev);
>  
> +	/* Pure DT lookup should use given supply name directly */
> +	if (!pure_dt_lookup)
> +		regulator_supply_alias(&dev, &supply);
> +
> +	if (!node && dev && dev->of_node)

The dev->of_node is redundant and with the above...

> +		node = dev->of_node;

...this becomes as simple as

	if (!node && dev)

> +	/* Pure DT lookup stops here. */
> +	if (pure_dt_lookup)
> +		return ERR_PTR(-ENODEV);

Looking at this pure_dt_lookup and the above (somehow inverted) case I would
rather use (node && !dev) or (!node && dev) explicitly everywhere.

...

> +struct regulator *_regulator_get(struct device *dev, struct device_node *node,
> +				 const char *id, enum regulator_get_type get_type)

Again, no of_ prefix and function becomes OF-specific...

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 3/6] i2c: Introduce OF component probe function
  2024-08-08  9:59 ` [PATCH v4 3/6] i2c: Introduce OF component probe function Chen-Yu Tsai
@ 2024-08-13 11:26   ` Andy Shevchenko
  2024-08-15  9:55     ` Chen-Yu Tsai
  0 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-13 11:26 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Thu, Aug 08, 2024 at 05:59:26PM +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 "failed-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.

...

> +int i2c_of_probe_component(struct device *dev, const char *type)

Use respective scoped variants and remove the related of_node_put() calls.

...

> +		ocs = kzalloc(sizeof(*ocs), GFP_KERNEL);

Use __free()

> +		if (!ocs) {
> +			ret = -ENOMEM;
> +			goto err_put_node;
> +		}

> +err_free_ocs:
> +	kfree(ocs);

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support
  2024-08-08  9:59 ` [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support Chen-Yu Tsai
@ 2024-08-13 11:41   ` Andy Shevchenko
  2024-08-14 11:34     ` Chen-Yu Tsai
  0 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-13 11:41 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Thu, Aug 08, 2024 at 05:59:27PM +0800, Chen-Yu Tsai wrote:
> This adds GPIO and regulator management to the I2C OF component prober.

Can this be two patches?

> Components that the prober intends to probe likely require their
> regulator supplies be enabled, and GPIOs be toggled to enable them or
> bring them out of reset before they will respond to probe attempts.
> 
> Without specific knowledge of each component's resource names or
> power sequencing requirements, the prober can only enable the
> regulator supplies all at once, and toggle the GPIOs all at once.
> Luckily, reset pins tend to be active low, while enable pins tend to
> be active high, so setting the raw status of all GPIO pins to high
> should work. The wait time before and after resources are enabled
> are collected from existing drivers and device trees.
> 
> The prober collects resources from all possible components and enables
> them together, instead of enabling resources and probing each component
> one by one. The latter approach does not provide any boot time benefits
> over simply enabling each component and letting each driver probe
> sequentially.
> 
> The prober will also deduplicate the resources, since on a component
> swap out or co-layout design, the resources are always the same.
> While duplicate regulator supplies won't cause much issue, shared
> GPIOs don't work reliably, especially with other drivers. For the
> same reason, the prober will release the GPIOs before the successfully
> probed component is actually enabled.

...

> +/*
> + * While 8 seems like a small number, especially when probing many component
> + * options, in practice all the options will have the same resources. The
> + * code getting the resources below does deduplication to avoid conflicts.
> + */
> +#define RESOURCE_MAX 8

Badly (broadly) named constant. Is it not the same that defines arguments in
the OF phandle lookup? Can you use that instead?

...

> +#define REGULATOR_SUFFIX "-supply"

Name is bad, also move '-' to the code, it's not part of the suffix, it's a
separator AFAICT.

...

> +	p = strstr(prop->name, REGULATOR_SUFFIX);

strstr()?! Are you sure it will have no side effects on some interesting names?

> +	if (!p)
> +		return 0;

> +	if (strcmp(p, REGULATOR_SUFFIX))
> +		return 0;

Ah, you do it this way...

What about




> +
> +	strscpy(con, prop->name, p - prop->name + 1);
> +	regulator = regulator_of_get_optional(node, con);
> +	/* DT lookup should never return -ENODEV */
> +	if (IS_ERR(regulator))
> +		return PTR_ERR(regulator);

...

> +	for (int i = 0; i < data->regulators_num; i++)

Why signed?

> +		if (regulator_is_equal(regulator, data->regulators[i])) {
> +			regulator_put(regulator);
> +			regulator = NULL;
> +			break;
> +		}

...

> +#define GPIO_SUFFIX "-gpio"

Bad define name, and why not "gpios"?

...

> +	p = strstr(prop->name, GPIO_SUFFIX);
> +	if (p) {
> +		strscpy(con, prop->name, p - prop->name + 1);
> +		con_id = con;
> +	} else if (strcmp(prop->name, "gpio") && strcmp(prop->name, "gpios")) {
> +		return 0;

We have an array of these suffixes, please use it. If required make it exported
to the others.

> +	}

...

> +	ret = of_parse_phandle_with_args_map(node, prop->name, "gpio", 0, &phargs);
> +	if (ret)
> +		return ret;

> +	gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
> +	if (IS_ERR(gpiod)) {
> +		of_node_put(phargs.np);
> +		return PTR_ERR(gpiod);
> +	}

Try not to mix fwnode and OF specifics. You may rely on fwnode for GPIO completely.

> +	if (data->gpiods_num == ARRAY_SIZE(data->gpiods)) {
> +		of_node_put(phargs.np);
> +		gpiod_put(gpiod);
> +		return -ENOMEM;
> +	}

...

> +	for (int i = data->gpiods_num - 1; i >= 0; i--)
> +		gpiod_put(data->gpiods[i]);

This sounds like reinvention of gpiod_*_array() call.

...

> +	for (int i = data->regulators_num; i >= 0; i--)
> +		regulator_put(data->regulators[i]);

Bulk regulators?

...

> +	for_each_child_of_node_scoped(i2c_node, node) {

Eventually _scoped(), but...

> +		u32 addr;
> +
> +		if (!of_node_name_prefix(node, type))
> +			continue;
> +		if (of_property_read_u32(node, "reg", &addr))
> +			continue;
> +
> +		dev_dbg(dev, "Requesting resources for %pOF\n", node);
> +		ret = i2c_of_probe_get_res(dev, node, &data);
> +		if (ret) {

> +			of_node_put(i2c_node);

...huh?!

> +			return ret;
> +		}
> +	}

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober
  2024-08-08  9:59 ` [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober Chen-Yu Tsai
@ 2024-08-13 11:46   ` Andy Shevchenko
  2024-08-14 10:10     ` Chen-Yu Tsai
  0 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-13 11:46 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Thu, Aug 08, 2024 at 05:59:28PM +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 prober. For any 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 "failed-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.

...

> + * Copyright (c) 2023 Google LLC

At bare minimum we are in 2024 now.

...

> +#include <linux/array_size.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>

> +#include <linux/of.h>

Why?

> +#include <linux/platform_device.h>

...

> +	for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) {
> +		if (!of_machine_is_compatible(hw_prober_platforms[i].compatible))
> +			continue;

> +		int ret;

I didn't know we allow this kind of definition mix besides for-loop and
__free()... Can you point me out where this style change was discussed?

> +		ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data);
> +		/* Ignore unrecoverable errors and keep going through other probers */
> +		if (ret == -EPROBE_DEFER)
> +			return ret;
> +	}

...

> +static void chromeos_of_hw_prober_driver_exit(void)
> +{
> +	if (!chromeos_of_hw_prober_pdev)
> +		return;

First of all, this is dup for the next call, second, when may this conditional
be true?

> +	platform_device_unregister(chromeos_of_hw_prober_pdev);
> +	platform_driver_unregister(&chromeos_of_hw_prober_driver);
> +}
> +module_exit(chromeos_of_hw_prober_driver_exit);

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string
  2024-08-13 11:11   ` Andy Shevchenko
@ 2024-08-13 19:18     ` Rob Herring
  2024-08-14  4:26       ` Chen-Yu Tsai
  0 siblings, 1 reply; 20+ messages in thread
From: Rob Herring @ 2024-08-13 19:18 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Chen-Yu Tsai, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Tue, Aug 13, 2024 at 02:11:30PM +0300, Andy Shevchenko wrote:
> On Thu, Aug 08, 2024 at 05:59:24PM +0800, Chen-Yu Tsai wrote:
> > Add a helper function to add string property updates to an OF changeset.
> > This is similar to of_changeset_add_prop_string(), but instead of adding
> > the property (and failing if it exists), it will update the property.
> > 
> > This shall be used later in the DT hardware prober.
> 
> ...
> 
> > +int of_changeset_update_prop_string(struct of_changeset *ocs,
> > +				    struct device_node *np,
> > +				    const char *prop_name, const char *str)
> > +{
> > +	struct property prop;
> > +
> > +	prop.name = (char *)prop_name;
> > +	prop.length = strlen(str) + 1;
> > +	prop.value = (void *)str;
> 
> Is it the existing style in the file? Otherwise I often see style like this
> 
> 	struct property prop = {
> 		.name = (char *)prop_name;
> 		.length = strlen(str) + 1;
> 		.value = (void *)str;
> 	};
> 
> in the kernel (IRQ domain, platform core, ...).

Okay with me to use this style regardless of existing style.

Rob

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string
  2024-08-13 19:18     ` Rob Herring
@ 2024-08-14  4:26       ` Chen-Yu Tsai
  0 siblings, 0 replies; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-14  4:26 UTC (permalink / raw)
  To: Rob Herring, Andy Shevchenko
  Cc: Saravana Kannan, Matthias Brugger, AngeloGioacchino Del Regno,
	Wolfram Sang, Benson Leung, Tzung-Bi Shih, Mark Brown,
	Liam Girdwood, chrome-platform, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, Douglas Anderson, Johan Hovold,
	Jiri Kosina, linux-i2c

On Wed, Aug 14, 2024 at 3:18 AM Rob Herring <robh@kernel.org> wrote:
>
> On Tue, Aug 13, 2024 at 02:11:30PM +0300, Andy Shevchenko wrote:
> > On Thu, Aug 08, 2024 at 05:59:24PM +0800, Chen-Yu Tsai wrote:
> > > Add a helper function to add string property updates to an OF changeset.
> > > This is similar to of_changeset_add_prop_string(), but instead of adding
> > > the property (and failing if it exists), it will update the property.
> > >
> > > This shall be used later in the DT hardware prober.
> >
> > ...
> >
> > > +int of_changeset_update_prop_string(struct of_changeset *ocs,
> > > +                               struct device_node *np,
> > > +                               const char *prop_name, const char *str)
> > > +{
> > > +   struct property prop;
> > > +
> > > +   prop.name = (char *)prop_name;
> > > +   prop.length = strlen(str) + 1;
> > > +   prop.value = (void *)str;
> >
> > Is it the existing style in the file? Otherwise I often see style like this

Yeah. This was mostly copy pasted.

> >
> >       struct property prop = {
> >               .name = (char *)prop_name;
> >               .length = strlen(str) + 1;
> >               .value = (void *)str;
> >       };
> >
> > in the kernel (IRQ domain, platform core, ...).
>
> Okay with me to use this style regardless of existing style.

Ack. I'll update it to the more modern style.

ChenYu

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober
  2024-08-13 11:46   ` Andy Shevchenko
@ 2024-08-14 10:10     ` Chen-Yu Tsai
  2024-08-14 13:41       ` Andy Shevchenko
  0 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-14 10:10 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Tue, Aug 13, 2024 at 7:46 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Thu, Aug 08, 2024 at 05:59:28PM +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 prober. For any 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 "failed-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.
>
> ...
>
> > + * Copyright (c) 2023 Google LLC
>
> At bare minimum we are in 2024 now.

Ack.

> ...
>
> > +#include <linux/array_size.h>
> > +#include <linux/i2c.h>
> > +#include <linux/module.h>
>
> > +#include <linux/of.h>
>
> Why?

Might have been left over from previous work and squashed into the wrong
commit. Will remove.

> > +#include <linux/platform_device.h>
>
> ...
>
> > +     for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) {
> > +             if (!of_machine_is_compatible(hw_prober_platforms[i].compatible))
> > +                     continue;
>
> > +             int ret;
>
> I didn't know we allow this kind of definition mix besides for-loop and
> __free()... Can you point me out where this style change was discussed?

Will move to the top of the for loop block.

> > +             ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data);
> > +             /* Ignore unrecoverable errors and keep going through other probers */
> > +             if (ret == -EPROBE_DEFER)
> > +                     return ret;
> > +     }
>
> ...
>
> > +static void chromeos_of_hw_prober_driver_exit(void)
> > +{
> > +     if (!chromeos_of_hw_prober_pdev)
> > +             return;
>
> First of all, this is dup for the next call, second, when may this conditional
> be true?

When the module is loaded on a machine that doesn't match any entry,
neither the driver nor the device are registered. Hence the check.

Or maybe the proper way to handle it is to return -ENODEV or something?
I'll work towards that.

Thanks
ChenYu

> > +     platform_device_unregister(chromeos_of_hw_prober_pdev);
> > +     platform_driver_unregister(&chromeos_of_hw_prober_driver);
> > +}
> > +module_exit(chromeos_of_hw_prober_driver_exit);
>
> --
> With Best Regards,
> Andy Shevchenko
>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support
  2024-08-13 11:41   ` Andy Shevchenko
@ 2024-08-14 11:34     ` Chen-Yu Tsai
  2024-08-14 13:53       ` Andy Shevchenko
  0 siblings, 1 reply; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-14 11:34 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Tue, Aug 13, 2024 at 7:41 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Thu, Aug 08, 2024 at 05:59:27PM +0800, Chen-Yu Tsai wrote:
> > This adds GPIO and regulator management to the I2C OF component prober.
>
> Can this be two patches?

You mean one for GPIOs and one for regulators right? Sure.

> > Components that the prober intends to probe likely require their
> > regulator supplies be enabled, and GPIOs be toggled to enable them or
> > bring them out of reset before they will respond to probe attempts.
> >
> > Without specific knowledge of each component's resource names or
> > power sequencing requirements, the prober can only enable the
> > regulator supplies all at once, and toggle the GPIOs all at once.
> > Luckily, reset pins tend to be active low, while enable pins tend to
> > be active high, so setting the raw status of all GPIO pins to high
> > should work. The wait time before and after resources are enabled
> > are collected from existing drivers and device trees.
> >
> > The prober collects resources from all possible components and enables
> > them together, instead of enabling resources and probing each component
> > one by one. The latter approach does not provide any boot time benefits
> > over simply enabling each component and letting each driver probe
> > sequentially.
> >
> > The prober will also deduplicate the resources, since on a component
> > swap out or co-layout design, the resources are always the same.
> > While duplicate regulator supplies won't cause much issue, shared
> > GPIOs don't work reliably, especially with other drivers. For the
> > same reason, the prober will release the GPIOs before the successfully
> > probed component is actually enabled.
>
> ...
>
> > +/*
> > + * While 8 seems like a small number, especially when probing many component
> > + * options, in practice all the options will have the same resources. The
> > + * code getting the resources below does deduplication to avoid conflicts.
> > + */
> > +#define RESOURCE_MAX 8
>
> Badly (broadly) named constant. Is it not the same that defines arguments in
> the OF phandle lookup? Can you use that instead?

I'm not sure what you are referring to. This is how many unique instances
of a given resource (GPIOs or regulators) the prober will track.

MAX_TRACKED_RESOURCES maybe?

> ...
>
> > +#define REGULATOR_SUFFIX "-supply"
>
> Name is bad, also move '-' to the code, it's not part of the suffix, it's a
> separator AFAICT.

OF_REGULATOR_SUPPLY_SUFFIX then?

Also, "supply" is not a valid property. It is always "X-supply".
Having the '-' directly in the string makes things simpler, albeit
making the name slightly off.

> ...
>
> > +     p = strstr(prop->name, REGULATOR_SUFFIX);
>
> strstr()?! Are you sure it will have no side effects on some interesting names?
>
> > +     if (!p)
> > +             return 0;
>
> > +     if (strcmp(p, REGULATOR_SUFFIX))
> > +             return 0;
>
> Ah, you do it this way...
>
> What about

About? (feels like an unfinished comment)

Looking around, it seems I could just rename and export "is_supply_name()"
from drivers/regulator/of_regulator.c .

> > +
> > +     strscpy(con, prop->name, p - prop->name + 1);
> > +     regulator = regulator_of_get_optional(node, con);
> > +     /* DT lookup should never return -ENODEV */
> > +     if (IS_ERR(regulator))
> > +             return PTR_ERR(regulator);
>
> ...
>
> > +     for (int i = 0; i < data->regulators_num; i++)
>
> Why signed?

No real reason, other than being the same as the for loops in the
free functions.

> > +             if (regulator_is_equal(regulator, data->regulators[i])) {
> > +                     regulator_put(regulator);
> > +                     regulator = NULL;
> > +                     break;
> > +             }
>
> ...
>
> > +#define GPIO_SUFFIX "-gpio"
>
> Bad define name, and why not "gpios"?

"-gpio" in strstr() would match against both "foo-gpio" and "foo-gpios".
More like laziness.

> ...
>
> > +     p = strstr(prop->name, GPIO_SUFFIX);
> > +     if (p) {
> > +             strscpy(con, prop->name, p - prop->name + 1);
> > +             con_id = con;
> > +     } else if (strcmp(prop->name, "gpio") && strcmp(prop->name, "gpios")) {
> > +             return 0;
>
> We have an array of these suffixes, please use it. If required make it exported
> to the others.

OK. I'll rewrite this section with that then.

> > +     }
>
> ...
>
> > +     ret = of_parse_phandle_with_args_map(node, prop->name, "gpio", 0, &phargs);
> > +     if (ret)
> > +             return ret;
>
> > +     gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
> > +     if (IS_ERR(gpiod)) {
> > +             of_node_put(phargs.np);
> > +             return PTR_ERR(gpiod);
> > +     }
>
> Try not to mix fwnode and OF specifics. You may rely on fwnode for GPIO completely.

Well, fwnode doesn't give a way to identify GPIOs without requesting them.

Instead I think I could first request GPIOs exclusively, and if that fails
try non-exclusive and see if that GPIO descriptor matches any known one.
If not then something in the DT is broken and it should error out. Then
the phandle parsing code could be dropped.

> > +     if (data->gpiods_num == ARRAY_SIZE(data->gpiods)) {
> > +             of_node_put(phargs.np);
> > +             gpiod_put(gpiod);
> > +             return -ENOMEM;
> > +     }
>
> ...
>
> > +     for (int i = data->gpiods_num - 1; i >= 0; i--)
> > +             gpiod_put(data->gpiods[i]);
>
> This sounds like reinvention of gpiod_*_array() call.

Indeed. Will switch to gpiod_put_array() with some slight tweaks.

> ...
>
> > +     for (int i = data->regulators_num; i >= 0; i--)
> > +             regulator_put(data->regulators[i]);
>
> Bulk regulators?

Bulk regulators API uses its own data structure which is not just an
array. So unlike gpiod_*_array() it can't be used in this case.

> ...
>
> > +     for_each_child_of_node_scoped(i2c_node, node) {
>
> Eventually _scoped(), but...
>
> > +             u32 addr;
> > +
> > +             if (!of_node_name_prefix(node, type))
> > +                     continue;
> > +             if (of_property_read_u32(node, "reg", &addr))
> > +                     continue;
> > +
> > +             dev_dbg(dev, "Requesting resources for %pOF\n", node);
> > +             ret = i2c_of_probe_get_res(dev, node, &data);
> > +             if (ret) {
>
> > +                     of_node_put(i2c_node);
>
> ...huh?!

Oops. This and the next "of_node_put(i2c_node)" should be
"i2c_put_adapter(i2c)" instead.


Thank you for the review. More stuff to try.


Regards
ChenYu

> > +                     return ret;
> > +             }
> > +     }
>
> --
> With Best Regards,
> Andy Shevchenko
>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober
  2024-08-14 10:10     ` Chen-Yu Tsai
@ 2024-08-14 13:41       ` Andy Shevchenko
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-14 13:41 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Wed, Aug 14, 2024 at 06:10:03PM +0800, Chen-Yu Tsai wrote:
> On Tue, Aug 13, 2024 at 7:46 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Thu, Aug 08, 2024 at 05:59:28PM +0800, Chen-Yu Tsai wrote:

...

> > > +static void chromeos_of_hw_prober_driver_exit(void)
> > > +{
> > > +     if (!chromeos_of_hw_prober_pdev)
> > > +             return;
> >
> > First of all, this is dup for the next call, second, when may this conditional
> > be true?
> 
> When the module is loaded on a machine that doesn't match any entry,
> neither the driver nor the device are registered. Hence the check.
> 
> Or maybe the proper way to handle it is to return -ENODEV or something?
> I'll work towards that.

The rule of thumb is the _exit() is called when your _init() finished with
success. This conditional seems confusing and likely reveals the logic issue
in _init(). Yes, _init() needs to return an error when there is no devices are
registered or expected to be registered. If there are devices that may appear
later on, this should be split to pure device driver and board file that
instantiate device for the known cases.

> > > +     platform_device_unregister(chromeos_of_hw_prober_pdev);
> > > +     platform_driver_unregister(&chromeos_of_hw_prober_driver);
> > > +}

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support
  2024-08-14 11:34     ` Chen-Yu Tsai
@ 2024-08-14 13:53       ` Andy Shevchenko
  2024-08-21  9:44         ` Chen-Yu Tsai
  0 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2024-08-14 13:53 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Wed, Aug 14, 2024 at 07:34:00PM +0800, Chen-Yu Tsai wrote:
> On Tue, Aug 13, 2024 at 7:41 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Thu, Aug 08, 2024 at 05:59:27PM +0800, Chen-Yu Tsai wrote:

...

> > > This adds GPIO and regulator management to the I2C OF component prober.

> > Can this be two patches?
> 
> You mean one for GPIOs and one for regulators right? Sure.

Yes.

...

> > > +#define RESOURCE_MAX 8
> >
> > Badly (broadly) named constant. Is it not the same that defines arguments in
> > the OF phandle lookup? Can you use that instead?
> 
> I'm not sure what you are referring to. This is how many unique instances
> of a given resource (GPIOs or regulators) the prober will track.
> 
> MAX_TRACKED_RESOURCES maybe?

Better, but still ambiguous. We have a namespace approach, something like
I2C_PROBER_... I have checked the existing constant and it's not what you
want, so forget about that part, only name of the definition is questionable.

...

> > > +#define REGULATOR_SUFFIX "-supply"
> >
> > Name is bad, also move '-' to the code, it's not part of the suffix, it's a
> > separator AFAICT.
> 
> OF_REGULATOR_SUPPLY_SUFFIX then?
> 
> Also, "supply" is not a valid property. It is always "X-supply".
> Having the '-' directly in the string makes things simpler, albeit
> making the name slightly off.

Let's use proper SUFFIX and separator separately.

#define I2C_PROBER_..._SUFFIX "supply"

(or alike)

...

> > > +     p = strstr(prop->name, REGULATOR_SUFFIX);
> >
> > strstr()?! Are you sure it will have no side effects on some interesting names?
> >
> > > +     if (!p)
> > > +             return 0;
> >
> > > +     if (strcmp(p, REGULATOR_SUFFIX))
> > > +             return 0;
> >
> > Ah, you do it this way...
> >
> > What about
> 
> About? (feels like an unfinished comment)

Yes, sorry for that. Since you found a better alternative, no need to finish
this part :-)

> Looking around, it seems I could just rename and export "is_supply_name()"
> from drivers/regulator/of_regulator.c .

Even better!

Something similar most likely can be done with GPIO (if not, we are always open
to the ideas how to deduplicate the code).

...

> > > +#define GPIO_SUFFIX "-gpio"
> >
> > Bad define name, and why not "gpios"?
> 
> "-gpio" in strstr() would match against both "foo-gpio" and "foo-gpios".
> More like laziness.

And opens can of worms with whatever ending of the property name.
Again, let's have something that GPIO library provides for everybody.

...

> > > +     ret = of_parse_phandle_with_args_map(node, prop->name, "gpio", 0, &phargs);
> > > +     if (ret)
> > > +             return ret;

(1)

> > > +     gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
> > > +     if (IS_ERR(gpiod)) {
> > > +             of_node_put(phargs.np);
> > > +             return PTR_ERR(gpiod);
> > > +     }
> >
> > Try not to mix fwnode and OF specifics. You may rely on fwnode for GPIO completely.
> 
> Well, fwnode doesn't give a way to identify GPIOs without requesting them.
> 
> Instead I think I could first request GPIOs exclusively, and if that fails
> try non-exclusive and see if that GPIO descriptor matches any known one.
> If not then something in the DT is broken and it should error out. Then
> the phandle parsing code could be dropped.

What I meant, the, e.g., (1) can be rewritten using fwnode API, but if you know
better way of doing things, then go for it.

> > > +     if (data->gpiods_num == ARRAY_SIZE(data->gpiods)) {
> > > +             of_node_put(phargs.np);
> > > +             gpiod_put(gpiod);
> > > +             return -ENOMEM;
> > > +     }

...

> > > +     for (int i = data->regulators_num; i >= 0; i--)
> > > +             regulator_put(data->regulators[i]);
> >
> > Bulk regulators?
> 
> Bulk regulators API uses its own data structure which is not just an
> array. So unlike gpiod_*_array() it can't be used in this case.

But it sounds like a bulk regulator case...
Whatever, it's Mark's area and he might suggest something better.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 3/6] i2c: Introduce OF component probe function
  2024-08-13 11:26   ` Andy Shevchenko
@ 2024-08-15  9:55     ` Chen-Yu Tsai
  0 siblings, 0 replies; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-15  9:55 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Tue, Aug 13, 2024 at 7:26 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Thu, Aug 08, 2024 at 05:59:26PM +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 "failed-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.
>
> ...
>
> > +int i2c_of_probe_component(struct device *dev, const char *type)
>
> Use respective scoped variants and remove the related of_node_put() calls.

Ack. Will also try splitting and reworking the code so they have
tighter scopes.

> ...
>
> > +             ocs = kzalloc(sizeof(*ocs), GFP_KERNEL);
>
> Use __free()

This ends up only a bit better than with gotos once this small section
is split out into a helper function.

AFAIK ocs, or at least the underlying properties, have to be left around
once the changeset is applied as they are now part of the dynamic live
tree. And that's fine since once applied, nothing is going to un-apply
it. OTOH in the error path it needs extra cleanup if any actions were
added.

So I end up with the following to silence the "must check return value"
warning on success, and cleanup on error:

ret = of_changeset_apply(ocs);
if (!ret) {
        void *ptr __always_unused = no_free_ptr(ocs);
} else {
        of_changeset_destroy(ocs);
}

In my case it might actually be safe to do of_changeset_destroy(ocs) and
free it regardless, but I'm not really confident.


ChenYu



> > +             if (!ocs) {
> > +                     ret = -ENOMEM;
> > +                     goto err_put_node;
> > +             }
>
> > +err_free_ocs:
> > +     kfree(ocs);
>
> --
> With Best Regards,
> Andy Shevchenko
>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support
  2024-08-14 13:53       ` Andy Shevchenko
@ 2024-08-21  9:44         ` Chen-Yu Tsai
  0 siblings, 0 replies; 20+ messages in thread
From: Chen-Yu Tsai @ 2024-08-21  9:44 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Saravana Kannan, Matthias Brugger,
	AngeloGioacchino Del Regno, Wolfram Sang, Benson Leung,
	Tzung-Bi Shih, Mark Brown, Liam Girdwood, chrome-platform,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Douglas Anderson, Johan Hovold, Jiri Kosina, linux-i2c

On Wed, Aug 14, 2024 at 9:53 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Wed, Aug 14, 2024 at 07:34:00PM +0800, Chen-Yu Tsai wrote:
> > On Tue, Aug 13, 2024 at 7:41 PM Andy Shevchenko
> > <andriy.shevchenko@linux.intel.com> wrote:
> > > On Thu, Aug 08, 2024 at 05:59:27PM +0800, Chen-Yu Tsai wrote:
>
> ...
>
> > > > This adds GPIO and regulator management to the I2C OF component prober.
>
> > > Can this be two patches?
> >
> > You mean one for GPIOs and one for regulators right? Sure.
>
> Yes.
>
> ...
>
> > > > +#define RESOURCE_MAX 8
> > >
> > > Badly (broadly) named constant. Is it not the same that defines arguments in
> > > the OF phandle lookup? Can you use that instead?
> >
> > I'm not sure what you are referring to. This is how many unique instances
> > of a given resource (GPIOs or regulators) the prober will track.
> >
> > MAX_TRACKED_RESOURCES maybe?
>
> Better, but still ambiguous. We have a namespace approach, something like
> I2C_PROBER_... I have checked the existing constant and it's not what you
> want, so forget about that part, only name of the definition is questionable.
>
> ...
>
> > > > +#define REGULATOR_SUFFIX "-supply"
> > >
> > > Name is bad, also move '-' to the code, it's not part of the suffix, it's a
> > > separator AFAICT.
> >
> > OF_REGULATOR_SUPPLY_SUFFIX then?
> >
> > Also, "supply" is not a valid property. It is always "X-supply".
> > Having the '-' directly in the string makes things simpler, albeit
> > making the name slightly off.
>
> Let's use proper SUFFIX and separator separately.
>
> #define I2C_PROBER_..._SUFFIX "supply"
>
> (or alike)
>
> ...
>
> > > > +     p = strstr(prop->name, REGULATOR_SUFFIX);
> > >
> > > strstr()?! Are you sure it will have no side effects on some interesting names?
> > >
> > > > +     if (!p)
> > > > +             return 0;
> > >
> > > > +     if (strcmp(p, REGULATOR_SUFFIX))
> > > > +             return 0;
> > >
> > > Ah, you do it this way...
> > >
> > > What about
> >
> > About? (feels like an unfinished comment)
>
> Yes, sorry for that. Since you found a better alternative, no need to finish
> this part :-)
>
> > Looking around, it seems I could just rename and export "is_supply_name()"
> > from drivers/regulator/of_regulator.c .
>
> Even better!
>
> Something similar most likely can be done with GPIO (if not, we are always open
> to the ideas how to deduplicate the code).
>
> ...

I ended up reworking around of_regulator_bulk_get_all(). See below.

> > > > +#define GPIO_SUFFIX "-gpio"
> > >
> > > Bad define name, and why not "gpios"?
> >
> > "-gpio" in strstr() would match against both "foo-gpio" and "foo-gpios".
> > More like laziness.
>
> And opens can of worms with whatever ending of the property name.
> Again, let's have something that GPIO library provides for everybody.

Ack.

> ...
>
> > > > +     ret = of_parse_phandle_with_args_map(node, prop->name, "gpio", 0, &phargs);
> > > > +     if (ret)
> > > > +             return ret;
>
> (1)
>
> > > > +     gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
> > > > +     if (IS_ERR(gpiod)) {
> > > > +             of_node_put(phargs.np);
> > > > +             return PTR_ERR(gpiod);
> > > > +     }
> > >
> > > Try not to mix fwnode and OF specifics. You may rely on fwnode for GPIO completely.
> >
> > Well, fwnode doesn't give a way to identify GPIOs without requesting them.
> >
> > Instead I think I could first request GPIOs exclusively, and if that fails
> > try non-exclusive and see if that GPIO descriptor matches any known one.
> > If not then something in the DT is broken and it should error out. Then
> > the phandle parsing code could be dropped.
>
> What I meant, the, e.g., (1) can be rewritten using fwnode API, but if you know
> better way of doing things, then go for it.
>
> > > > +     if (data->gpiods_num == ARRAY_SIZE(data->gpiods)) {
> > > > +             of_node_put(phargs.np);
> > > > +             gpiod_put(gpiod);
> > > > +             return -ENOMEM;
> > > > +     }

After reworking around |struct gpio_descs|, i.e. gpio_*_array() APIs,
this ended up being the only bit that actually used the _array variant
with gpiod_put_array(). The other bits remained for-loop-based.

> ...
>
> > > > +     for (int i = data->regulators_num; i >= 0; i--)
> > > > +             regulator_put(data->regulators[i]);
> > >
> > > Bulk regulators?
> >
> > Bulk regulators API uses its own data structure which is not just an
> > array. So unlike gpiod_*_array() it can't be used in this case.
>
> But it sounds like a bulk regulator case...
> Whatever, it's Mark's area and he might suggest something better.

There's of_regulator_bulk_get_all(), which has had no users since it was
merged. It gets all supplies under one device node and gives a handle for
the bulk regulator API. After reworking it to do lookups directly from
DT, all the regulator stuff can be converted to the bulk regulator API.
There's no deduplication anymore, though it's not a huge problem given
that regulators are reference counted.

I'll do one last pass over the code and send out a new version tomorrow.

Thanks
ChenYu

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2024-08-21  9:44 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-08  9:59 [PATCH v4 0/6] platform/chrome: Introduce DT hardware prober Chen-Yu Tsai
2024-08-08  9:59 ` [PATCH v4 1/6] of: dynamic: Add of_changeset_update_prop_string Chen-Yu Tsai
2024-08-13 11:11   ` Andy Shevchenko
2024-08-13 19:18     ` Rob Herring
2024-08-14  4:26       ` Chen-Yu Tsai
2024-08-08  9:59 ` [PATCH v4 2/6] regulator: Add regulator_of_get_optional() for pure DT regulator lookup Chen-Yu Tsai
2024-08-13 11:22   ` Andy Shevchenko
2024-08-08  9:59 ` [PATCH v4 3/6] i2c: Introduce OF component probe function Chen-Yu Tsai
2024-08-13 11:26   ` Andy Shevchenko
2024-08-15  9:55     ` Chen-Yu Tsai
2024-08-08  9:59 ` [PATCH v4 4/6] i2c: of-prober: Add GPIO and regulator support Chen-Yu Tsai
2024-08-13 11:41   ` Andy Shevchenko
2024-08-14 11:34     ` Chen-Yu Tsai
2024-08-14 13:53       ` Andy Shevchenko
2024-08-21  9:44         ` Chen-Yu Tsai
2024-08-08  9:59 ` [PATCH v4 5/6] platform/chrome: Introduce device tree hardware prober Chen-Yu Tsai
2024-08-13 11:46   ` Andy Shevchenko
2024-08-14 10:10     ` Chen-Yu Tsai
2024-08-14 13:41       ` Andy Shevchenko
2024-08-08  9:59 ` [PATCH v4 6/6] arm64: dts: mediatek: mt8173-elm-hana: Mark touchscreens and trackpads as fail Chen-Yu Tsai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).