summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorChen-Yu Tsai <wenst@chromium.org>2024-11-06 17:33:32 +0800
committerWolfram Sang <wsa+renesas@sang-engineering.com>2024-11-27 12:04:26 +0100
commit39b415f84654892003cebb7c026b7daa3380610b (patch)
tree0dea4902ff0f4eab48da6ed9a4e1fa5fae21f304 /drivers/i2c
parent897261149d255d03fc90bec6782e3835cacbfdde (diff)
downloadlwn-39b415f84654892003cebb7c026b7daa3380610b.tar.gz
lwn-39b415f84654892003cebb7c026b7daa3380610b.zip
i2c: of-prober: Add GPIO support to simple helpers
Add GPIO support to the simple helpers for 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. Regulator supplies were handled in the previous patch. The assumption is that the same class of components to be probed are always connected in the same fashion with the same regulator supply and GPIO. The names may vary due to binding differences, but the physical layout does not change. This supports at most one GPIO pin. The user must specify the GPIO name, the polarity, and the amount of time to wait after the GPIO is toggled. Devices with more than one GPIO pin likely require specific power sequencing beyond what generic code can easily support. Signed-off-by: Chen-Yu Tsai <wenst@chromium.org> Reviewed-by: Douglas Anderson <dianders@chromium.org> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Tested-by: Andrey Skvortsov <andrej.skvortzov@gmail.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/i2c-core-of-prober.c100
1 files changed, 98 insertions, 2 deletions
diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
index 76df75c51cae..b9ca785f8b17 100644
--- a/drivers/i2c/i2c-core-of-prober.c
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-of-prober.h>
#include <linux/module.h>
@@ -31,7 +32,6 @@
* address responds.
*
* TODO:
- * - Support handling common GPIOs.
* - Support I2C muxes
*/
@@ -246,6 +246,62 @@ static void i2c_of_probe_simple_disable_regulator(struct device *dev, struct i2c
regulator_disable(ctx->supply);
}
+static int i2c_of_probe_simple_get_gpiod(struct device *dev, struct device_node *node,
+ struct i2c_of_probe_simple_ctx *ctx)
+{
+ struct fwnode_handle *fwnode = of_fwnode_handle(node);
+ struct gpio_desc *gpiod;
+ const char *con_id;
+
+ /* NULL signals no GPIO needed */
+ if (!ctx->opts->gpio_name)
+ return 0;
+
+ /* An empty string signals an unnamed GPIO */
+ if (!ctx->opts->gpio_name[0])
+ con_id = NULL;
+ else
+ con_id = ctx->opts->gpio_name;
+
+ gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+
+ ctx->gpiod = gpiod;
+
+ return 0;
+}
+
+static void i2c_of_probe_simple_put_gpiod(struct i2c_of_probe_simple_ctx *ctx)
+{
+ gpiod_put(ctx->gpiod);
+ ctx->gpiod = NULL;
+}
+
+static int i2c_of_probe_simple_set_gpio(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+{
+ int ret;
+
+ if (!ctx->gpiod)
+ return 0;
+
+ dev_dbg(dev, "Configuring GPIO\n");
+
+ ret = gpiod_direction_output(ctx->gpiod, ctx->opts->gpio_assert_to_enable);
+ if (ret)
+ return ret;
+
+ if (ctx->opts->post_gpio_config_delay_ms)
+ msleep(ctx->opts->post_gpio_config_delay_ms);
+
+ return 0;
+}
+
+static void i2c_of_probe_simple_disable_gpio(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+{
+ gpiod_set_value(ctx->gpiod, !ctx->opts->gpio_assert_to_enable);
+}
+
/**
* i2c_of_probe_simple_enable - Simple helper for I2C OF prober to get and enable resources
* @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages
@@ -253,7 +309,11 @@ static void i2c_of_probe_simple_disable_regulator(struct device *dev, struct i2c
* @data: Pointer to &struct i2c_of_probe_simple_ctx helper context.
*
* If &i2c_of_probe_simple_opts->supply_name is given, request the named regulator supply.
+ * If &i2c_of_probe_simple_opts->gpio_name is given, request the named GPIO. Or if it is
+ * the empty string, request the unnamed GPIO.
* If a regulator supply was found, enable that regulator.
+ * If a GPIO line was found, configure the GPIO line to output and set value
+ * according to given options.
*
* Return: %0 on success or no-op, or a negative error number on failure.
*/
@@ -282,12 +342,24 @@ int i2c_of_probe_simple_enable(struct device *dev, struct device_node *bus_node,
if (ret)
goto out_put_node;
- ret = i2c_of_probe_simple_enable_regulator(dev, ctx);
+ ret = i2c_of_probe_simple_get_gpiod(dev, node, ctx);
if (ret)
goto out_put_supply;
+ ret = i2c_of_probe_simple_enable_regulator(dev, ctx);
+ if (ret)
+ goto out_put_gpiod;
+
+ ret = i2c_of_probe_simple_set_gpio(dev, ctx);
+ if (ret)
+ goto out_disable_regulator;
+
return 0;
+out_disable_regulator:
+ i2c_of_probe_simple_disable_regulator(dev, ctx);
+out_put_gpiod:
+ i2c_of_probe_simple_put_gpiod(ctx);
out_put_supply:
i2c_of_probe_simple_put_supply(ctx);
out_put_node:
@@ -297,16 +369,39 @@ out_put_node:
EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_enable, I2C_OF_PROBER);
/**
+ * i2c_of_probe_simple_cleanup_early - \
+ * Simple helper for I2C OF prober to release GPIOs before component is enabled
+ * @dev: Pointer to the &struct device of the caller; unused.
+ * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context.
+ *
+ * GPIO descriptors are exclusive and have to be released before the
+ * actual driver probes so that the latter can acquire them.
+ */
+void i2c_of_probe_simple_cleanup_early(struct device *dev, void *data)
+{
+ struct i2c_of_probe_simple_ctx *ctx = data;
+
+ i2c_of_probe_simple_put_gpiod(ctx);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup_early, I2C_OF_PROBER);
+
+/**
* i2c_of_probe_simple_cleanup - Clean up and release resources for I2C OF prober simple helpers
* @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages
* @data: Pointer to &struct i2c_of_probe_simple_ctx helper context.
*
+ * * If a GPIO line was found and not yet released, set its value to the opposite of that
+ * set in i2c_of_probe_simple_enable() and release it.
* * If a regulator supply was found, disable that regulator and release it.
*/
void i2c_of_probe_simple_cleanup(struct device *dev, void *data)
{
struct i2c_of_probe_simple_ctx *ctx = data;
+ /* GPIO operations here are no-ops if i2c_of_probe_simple_cleanup_early was called. */
+ i2c_of_probe_simple_disable_gpio(dev, ctx);
+ i2c_of_probe_simple_put_gpiod(ctx);
+
i2c_of_probe_simple_disable_regulator(dev, ctx);
i2c_of_probe_simple_put_supply(ctx);
}
@@ -314,6 +409,7 @@ EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup, I2C_OF_PROBER);
struct i2c_of_probe_ops i2c_of_probe_simple_ops = {
.enable = i2c_of_probe_simple_enable,
+ .cleanup_early = i2c_of_probe_simple_cleanup_early,
.cleanup = i2c_of_probe_simple_cleanup,
};
EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_ops, I2C_OF_PROBER);