[PATCH 2/2] mmc: pwrseq_simple: add support for reset control

From: Catalin Popescu
Date: Fri Oct 04 2024 - 08:08:16 EST


Reset controls are refcounted and as such allows to use shared resets or
gpios across drivers. Add support for reset control while keeping in
place the support for gpios. Use compatible string to choose between
reset control ("mmc-pwrseq-simple-reset") and reset gpios
("mmc-pwrseq-simple"). Only one reset control is supported.

Signed-off-by: Catalin Popescu <catalin.popescu@xxxxxxxxxxxxxxxxxxxx>
---
drivers/mmc/core/pwrseq_simple.c | 58 ++++++++++++++++++++++++++------
1 file changed, 48 insertions(+), 10 deletions(-)

diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 9e016b0746f5..6495b97d48d9 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -17,11 +17,17 @@
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/property.h>
+#include <linux/of.h>
+#include <linux/reset.h>

#include <linux/mmc/host.h>

#include "pwrseq.h"

+struct mmc_pwrseq_simple_data {
+ bool use_reset;
+};
+
struct mmc_pwrseq_simple {
struct mmc_pwrseq pwrseq;
bool clk_enabled;
@@ -29,6 +35,8 @@ struct mmc_pwrseq_simple {
u32 power_off_delay_us;
struct clk *ext_clk;
struct gpio_descs *reset_gpios;
+ struct reset_control *reset_ctrl;
+ const struct mmc_pwrseq_simple_data *data;
};

#define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq)
@@ -67,14 +75,21 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
pwrseq->clk_enabled = true;
}

- mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
+ if (pwrseq->data->use_reset) {
+ reset_control_deassert(pwrseq->reset_ctrl);
+ reset_control_assert(pwrseq->reset_ctrl);
+ } else
+ mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
}

static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
{
struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);

- mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
+ if (pwrseq->data->use_reset)
+ reset_control_deassert(pwrseq->reset_ctrl);
+ else
+ mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);

if (pwrseq->post_power_on_delay_ms)
msleep(pwrseq->post_power_on_delay_ms);
@@ -84,7 +99,10 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
{
struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);

- mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
+ if (pwrseq->data->use_reset)
+ reset_control_assert(pwrseq->reset_ctrl);
+ else
+ mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);

if (pwrseq->power_off_delay_us)
usleep_range(pwrseq->power_off_delay_us,
@@ -102,8 +120,17 @@ static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
.power_off = mmc_pwrseq_simple_power_off,
};

+struct mmc_pwrseq_simple_data mmc_pwrseq_simple_gpio = {
+ .use_reset = false,
+};
+
+struct mmc_pwrseq_simple_data mmc_pwrseq_simple_reset = {
+ .use_reset = true,
+};
+
static const struct of_device_id mmc_pwrseq_simple_of_match[] = {
- { .compatible = "mmc-pwrseq-simple",},
+ { .compatible = "mmc-pwrseq-simple", .data = &mmc_pwrseq_simple_gpio },
+ { .compatible = "mmc-pwrseq-simple-reset", .data = &mmc_pwrseq_simple_reset },
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match);
@@ -121,12 +148,23 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT)
return dev_err_probe(dev, PTR_ERR(pwrseq->ext_clk), "external clock not ready\n");

- pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset",
- GPIOD_OUT_HIGH);
- if (IS_ERR(pwrseq->reset_gpios) &&
- PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
- PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
- return dev_err_probe(dev, PTR_ERR(pwrseq->reset_gpios), "reset GPIOs not ready\n");
+ pwrseq->data = of_device_get_match_data(dev);
+ if (!pwrseq->data)
+ return -EINVAL;
+
+ if (pwrseq->data->use_reset) {
+ pwrseq->reset_ctrl = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(pwrseq->reset_ctrl))
+ return dev_err_probe(dev, PTR_ERR(pwrseq->reset_ctrl),
+ "reset controls not ready\n");
+ } else {
+ pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(pwrseq->reset_gpios) &&
+ PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
+ PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
+ return dev_err_probe(dev, PTR_ERR(pwrseq->reset_gpios),
+ "reset GPIOs not ready\n");
+ }
}

device_property_read_u32(dev, "post-power-on-delay-ms",
--
2.34.1