From nobody Mon Nov 25 05:48:57 2024 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 563E01E102E for ; Wed, 30 Oct 2024 07:22:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730272975; cv=none; b=NK34J7DdYiHkPxsrJbmjkya6Fm4kj5QJaqdfrYStlaIepQtKUWOCAfB07KR3IYPLU0RTMwDSg8VlavEyB42HhV/DSq35nVYt3OoGqNSxtrqGpI3FDOvnXaxS1ox6oVS+//l1a9rZnlBQ2bjOp65zU6GX9N3aZGY80yTuTpKWGVE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730272975; c=relaxed/simple; bh=VA/r07xCMV1/d7b2TbyzlX9Dhhfu/zkA57gqwPjWxhE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NNGkwdO/UVjISmlMQ2eL3t+8WDCmpCxMr7Qn/9tUdgafNOS5qa7FRuj9yZseYRtGV3Hc7twk0Q28DHCeNdymJtZWyOMon5Ni/BXDW+YWdkB0farNz48kr4iDeyDOKmizRyO/oEIw193SZicCWmPLbA8g+wlDCkNba5NlNKT6fmY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=CwVk2KQr; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="CwVk2KQr" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-20e6981ca77so67256925ad.2 for ; Wed, 30 Oct 2024 00:22:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1730272972; x=1730877772; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=iGV/n/Ugam46hql9btLSYEeBOG83+nolyClCQ9yXEuc=; b=CwVk2KQrOgh0W/wf3Be6+FjWlVttzhiAgtXhIalVsHbGiFpox/L4KS2o5SkPzQTnF5 hMvr5nAISZuXcM1Uet/Kxv49oheOBFNgtQIKWNxrQvM9GnLPlZ9uSbqcQ+CW7JGVWu3H q189iNq9zScHWMG+9gMJA7XzBE1a+DbUQiOgM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730272972; x=1730877772; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=iGV/n/Ugam46hql9btLSYEeBOG83+nolyClCQ9yXEuc=; b=EkWapHd8m/TNCWd7mwtki721gxe1SbzW/2mkIpx9sReK6FAxOMeL3S6UUUZ7ThR9WB ySoXv8UOy4h6BvKqqXPka4wxGi0H9T6J2Gowio7iylAMnO+UclW3bZgT4d3dRjabZPrT UbMCPjRtb/CNd/+w1sDhPhi3avbrBarH157yeyEA1zoBl3RJmmJf9MY9nEEViR7A4dbf 09sL/KywfLjZGHBKvuhYesxeapn6DcDwSWKwfiTkmUTKeiUTmyJTISfdut2I2CaKWJx5 2hA8V7dPjxtuSfmTOaXvOnNvzlXn6jp/0/x+VjCkHaF/V0MQ3lb3jxb5MWKogAxZAePA 787A== X-Forwarded-Encrypted: i=1; AJvYcCX5aIyao7N+4dVBZVrRHGEVWvCXbs6dkDBHUI6gf8IbDyepd9BV6poepdnwwdMpN5pOwuBaJxG4t1Jk6Og=@vger.kernel.org X-Gm-Message-State: AOJu0YzHa+8O3rFmgSdJ1S9xqe4fyDYJg0tPwMkfhdQ+ic/d8ot9Mxlj dCVJ4NeAz8HXIDzx5Oizh85HUh2UUiyWBFteNf6WIAMvPDLVI0Wvjm62uPg8bQq1tKskx/1HS1U = X-Google-Smtp-Source: AGHT+IEy1Vg0oRHVJFHBfqLUCfRXAUxJa8Aozk2EFlORmIleEz2egKAvzOhCZhekzAigBDhVAejjRA== X-Received: by 2002:a17:902:e80b:b0:20c:83e7:ca54 with SMTP id d9443c01a7336-210c6c06346mr162621725ad.27.1730272972509; Wed, 30 Oct 2024 00:22:52 -0700 (PDT) Received: from wenstp920.tpe.corp.google.com ([2401:fa00:1:10:d1f3:aa64:657f:d8a8]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-210bc02e67esm76641445ad.186.2024.10.30.00.22.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Oct 2024 00:22:52 -0700 (PDT) From: Chen-Yu Tsai To: Rob Herring , Saravana Kannan , Matthias Brugger , AngeloGioacchino Del Regno , Wolfram Sang Cc: Chen-Yu Tsai , Benson Leung , Tzung-Bi Shih , chrome-platform@lists.linux.dev, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Douglas Anderson , Johan Hovold , Jiri Kosina , Andy Shevchenko , linux-i2c@vger.kernel.org Subject: [PATCH v10 5/7] i2c: of-prober: Add GPIO support to simple helpers Date: Wed, 30 Oct 2024 15:22:26 +0800 Message-ID: <20241030072229.1013235-6-wenst@chromium.org> X-Mailer: git-send-email 2.47.0.163.g1226f6d8fa-goog In-Reply-To: <20241030072229.1013235-1-wenst@chromium.org> References: <20241030072229.1013235-1-wenst@chromium.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Douglas Anderson Reviewed-by: Andy Shevchenko --- Changes since v9: - Picked up Andy's reviewed-by Changes since v8: - Moved |struct gpio_desc| forward declaration before |struct regulator| (Andy) - Dropped GPIO descriptor validity check from i2c_of_probe_simple_disable_gpio(); GPIO functions check it (Andy) - Dropped comment from i2c_of_probe_simple_disable_gpio() (Doug) - Reworded comment in i2c_of_probe_simple_cleanup() (Doug) - Picked up Doug's reviewed-by Changes since v7: - Dropped mention of time unit in struct i2c_of_probe_simple_opts kernel doc (Andy) - Added check for non-zero delay before msleep() (Doug) - Simplified GPIO name check and reverse conditional branches (Andy) - Added description about the supported power sequence - Switched GPIO usage to logical levels (Doug) - Changed some variable names and comments to fit - Added description of power sequence to struct i2c_of_probe_simple_opts (Doug) - Added comment saying i2c_of_probe_simple_put_gpiod() might be no-op (Doug) - Combined callbacks (.get_resources with .enable; .cleanup with .free_resources_late); renamed i2c_of_probe_simple_free_res_early() to i2c_of_probe_simple_cleanup_early() Changes since v6: - Restructured into helpers for the I2C OF component prober - Reduced to only handle one GPIO - Set GPIO to input on (failure) cleanup - Updated commit message Changes since v5: - Renamed "con" to "propname" in i2c_of_probe_get_gpiod() - Copy string first and check return value of strscpy() for overflow in i2c_of_probe_get_gpiod() - Add parenthesis around "enable" and "reset" GPIO names in comments - Split resource count debug message into two separate lines - Split out GPIO helper from i2c_of_probe_enable_res() to keep code cleaner following the previous patch - Adopted options for customizing power sequencing delay following previous patch Changes since v4: - Split out from previous patch - Moved GPIO property name check to common function in gpiolib.c in new patch - Moved i2c_of_probe_free_gpios() into for_each_child_of_node_scoped() - Rewrote in gpiod_*_array-esque fashion --- drivers/i2c/i2c-core-of-prober.c | 100 ++++++++++++++++++++++++++++++- include/linux/i2c-of-prober.h | 20 +++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-pro= ber.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 #include #include +#include #include #include #include @@ -31,7 +32,6 @@ * address responds. * * TODO: - * - Support handling common GPIOs. * - Support I2C muxes */ =20 @@ -246,6 +246,62 @@ static void i2c_of_probe_simple_disable_regulator(stru= ct device *dev, struct i2c regulator_disable(ctx->supply); } =20 +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 =3D 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 =3D NULL; + else + con_id =3D ctx->opts->gpio_name; + + gpiod =3D fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-p= rober"); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + + ctx->gpiod =3D gpiod; + + return 0; +} + +static void i2c_of_probe_simple_put_gpiod(struct i2c_of_probe_simple_ctx *= ctx) +{ + gpiod_put(ctx->gpiod); + ctx->gpiod =3D 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 =3D gpiod_direction_output(ctx->gpiod, ctx->opts->gpio_assert_to_enab= le); + 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 i2= c_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_pr= intk() messages @@ -253,7 +309,11 @@ static void i2c_of_probe_simple_disable_regulator(stru= ct 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 r= egulator supply. + * If &i2c_of_probe_simple_opts->gpio_name is given, request the named GPI= O. 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 val= ue + * 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, st= ruct device_node *bus_node, if (ret) goto out_put_node; =20 - ret =3D i2c_of_probe_simple_enable_regulator(dev, ctx); + ret =3D i2c_of_probe_simple_get_gpiod(dev, node, ctx); if (ret) goto out_put_supply; =20 + ret =3D i2c_of_probe_simple_enable_regulator(dev, ctx); + if (ret) + goto out_put_gpiod; + + ret =3D i2c_of_probe_simple_set_gpio(dev, ctx); + if (ret) + goto out_disable_regulator; + return 0; =20 +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: @@ -296,17 +368,40 @@ int i2c_of_probe_simple_enable(struct device *dev, st= ruct device_node *bus_node, } EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_enable, I2C_OF_PROBER); =20 +/** + * i2c_of_probe_simple_cleanup_early - \ + * Simple helper for I2C OF prober to release GPIOs before component is en= abled + * @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 =3D 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_pr= intk() 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 o= pposite of that + * set in i2c_of_probe_simple_enable() and release it. * * If a regulator supply was found, disable that regulator and release i= t. */ void i2c_of_probe_simple_cleanup(struct device *dev, void *data) { struct i2c_of_probe_simple_ctx *ctx =3D data; =20 + /* GPIO operations here are no-ops if i2c_of_probe_simple_cleanup_early w= as 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_O= F_PROBER); =20 struct i2c_of_probe_ops i2c_of_probe_simple_ops =3D { .enable =3D i2c_of_probe_simple_enable, + .cleanup_early =3D i2c_of_probe_simple_cleanup_early, .cleanup =3D i2c_of_probe_simple_cleanup, }; EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_ops, I2C_OF_PROBER); diff --git a/include/linux/i2c-of-prober.h b/include/linux/i2c-of-prober.h index df95aa6ad90e..a232f3cbb252 100644 --- a/include/linux/i2c-of-prober.h +++ b/include/linux/i2c-of-prober.h @@ -85,6 +85,7 @@ int i2c_of_probe_component(struct device *dev, const stru= ct i2c_of_probe_cfg *cf * * The following helpers are provided: * * i2c_of_probe_simple_enable() + * * i2c_of_probe_simple_cleanup_early() * * i2c_of_probe_simple_cleanup() */ =20 @@ -92,14 +93,31 @@ int i2c_of_probe_component(struct device *dev, const st= ruct i2c_of_probe_cfg *cf * struct i2c_of_probe_simple_opts - Options for simple I2C component prob= er callbacks * @res_node_compatible: Compatible string of device node to retrieve reso= urces from. * @supply_name: Name of regulator supply. + * @gpio_name: Name of GPIO. NULL if no GPIO line is used. Empty string ("= ") if GPIO + * line is unnamed. * @post_power_on_delay_ms: Delay after regulators are powered on. Passed = to msleep(). + * @post_gpio_config_delay_ms: Delay after GPIO is configured. Passed to m= sleep(). + * @gpio_assert_to_enable: %true if GPIO should be asserted, i.e. set to l= ogical high, + * to enable the component. + * + * This describes power sequences common for the class of components suppo= rted by the + * simple component prober: + * * @gpio_name is configured to the non-active setting according to @gpio= _assert_to_enable. + * * @supply_name regulator supply is enabled. + * * Wait for @post_power_on_delay_ms to pass. + * * @gpio_name is configured to the active setting according to @gpio_ass= ert_to_enable. + * * Wait for @post_gpio_config_delay_ms to pass. */ struct i2c_of_probe_simple_opts { const char *res_node_compatible; const char *supply_name; + const char *gpio_name; unsigned int post_power_on_delay_ms; + unsigned int post_gpio_config_delay_ms; + bool gpio_assert_to_enable; }; =20 +struct gpio_desc; struct regulator; =20 struct i2c_of_probe_simple_ctx { @@ -107,9 +125,11 @@ struct i2c_of_probe_simple_ctx { const struct i2c_of_probe_simple_opts *opts; /* private: internal fields for helpers. */ struct regulator *supply; + struct gpio_desc *gpiod; }; =20 int i2c_of_probe_simple_enable(struct device *dev, struct device_node *bus= _node, void *data); +void i2c_of_probe_simple_cleanup_early(struct device *dev, void *data); void i2c_of_probe_simple_cleanup(struct device *dev, void *data); =20 extern struct i2c_of_probe_ops i2c_of_probe_simple_ops; --=20 2.47.0.163.g1226f6d8fa-goog