All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] mmc: core: hw_reset changes
@ 2014-11-24 11:06 Johan Rudholm
  2014-11-24 11:06 ` [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops Johan Rudholm
  2014-11-24 11:06 ` [PATCH v4 2/2] mmc: sd: add hw_reset callback Johan Rudholm
  0 siblings, 2 replies; 9+ messages in thread
From: Johan Rudholm @ 2014-11-24 11:06 UTC (permalink / raw)
  To: linux-mmc, Chris Ball, Ulf Hansson
  Cc: Adrian Hunter, Guennadi Liakhovetski, David Lanzendörfer,
	Jesper Nilsson, Johan Rudholm

Make the mmc_hw_reset routines more generic, so we can easily add a
power cycle of SD cards as well. Also simplify the (e)MMC specific
parts of the reset code.

As I don't have an eMMC device myself, much less one with a reset line,
I'd be very happy if someone could help me test the code with an eMMC?

v4:
 - Rebase onto next

v3:
 - Keep mmc_can_reset
 - Always set bus_mode = MMC_BUSMODE_PUSHPULL in mmc_set_initial_state()

v2:
 - Call the new bus_ops member hw_reset instead of power_reset
 - Create mmc_set_initial_state and call it from mmc_mmc_hw_reset
   instead of mmc_power_up
 - Keep "mmc_hw_reset" naming

Johan Rudholm (2):
  mmc: core: turn hw_reset into a bus_ops
  mmc: sd: add hw_reset callback

 drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
 drivers/mmc/core/core.h |    5 ++++
 drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
 drivers/mmc/core/sd.c   |   13 +++++++++++
 4 files changed, 76 insertions(+), 38 deletions(-)

-- 
1.7.2.5


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

* [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-11-24 11:06 [PATCH v4 0/2] mmc: core: hw_reset changes Johan Rudholm
@ 2014-11-24 11:06 ` Johan Rudholm
  2014-11-25 13:48   ` Ulf Hansson
  2014-11-24 11:06 ` [PATCH v4 2/2] mmc: sd: add hw_reset callback Johan Rudholm
  1 sibling, 1 reply; 9+ messages in thread
From: Johan Rudholm @ 2014-11-24 11:06 UTC (permalink / raw)
  To: linux-mmc, Chris Ball, Ulf Hansson
  Cc: Adrian Hunter, Guennadi Liakhovetski, David Lanzendörfer,
	Jesper Nilsson, Johan Rudholm

Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
it from the new bus_ops member hw_reset. This also lets us add code
for resetting SD cards as well.

Signed-off-by: Johan Rudholm <johanru@axis.com>
---
 drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
 drivers/mmc/core/core.h |    5 ++++
 drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 38 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9584bff..492b3e5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
 	mmc_host_clk_release(host);
 }
 
-int mmc_can_reset(struct mmc_card *card)
-{
-	u8 rst_n_function;
-
-	if (!mmc_card_mmc(card))
-		return 0;
-	rst_n_function = card->ext_csd.rst_n_function;
-	if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
-		return 0;
-	return 1;
-}
-EXPORT_SYMBOL(mmc_can_reset);
-
+/* Reset card in a bus-specific way */
 static int mmc_do_hw_reset(struct mmc_host *host, int check)
 {
-	struct mmc_card *card = host->card;
-
-	if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
-		return -EOPNOTSUPP;
+	int ret;
 
-	if (!card)
+	if (!host->card)
 		return -EINVAL;
 
-	if (!mmc_can_reset(card))
+	if (!host->bus_ops || !host->bus_ops->hw_reset ||
+			host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
 		return -EOPNOTSUPP;
 
-	mmc_host_clk_hold(host);
-	mmc_set_clock(host, host->f_init);
+	ret = host->bus_ops->hw_reset(host, check);
 
-	host->ops->hw_reset(host);
-
-	/* If the reset has happened, then a status command will fail */
-	if (check) {
-		u32 status;
-
-		if (!mmc_send_status(card, &status)) {
-			mmc_host_clk_release(host);
-			return -ENOSYS;
-		}
-	}
-
-	/* Set initial state and call mmc_set_ios */
-	mmc_set_initial_state(host);
+	if (check == MMC_HW_RESET_TEST_CARD)
+		return ret;
 
-	mmc_host_clk_release(host);
+	pr_warn("%s: tried to reset card (status %d)\n",
+						mmc_hostname(host), ret);
 
 	return host->bus_ops->power_restore(host);
 }
 
 int mmc_hw_reset(struct mmc_host *host)
 {
-	return mmc_do_hw_reset(host, 0);
+	return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
 }
 EXPORT_SYMBOL(mmc_hw_reset);
 
 int mmc_hw_reset_check(struct mmc_host *host)
 {
-	return mmc_do_hw_reset(host, 1);
+	return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
 }
 EXPORT_SYMBOL(mmc_hw_reset_check);
 
+int mmc_can_reset(struct mmc_card *card)
+{
+	return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
+}
+EXPORT_SYMBOL(mmc_can_reset);
+
 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 {
 	host->f_init = freq;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index d76597c..f6e0a52 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -27,6 +27,11 @@ struct mmc_bus_ops {
 	int (*power_restore)(struct mmc_host *);
 	int (*alive)(struct mmc_host *);
 	int (*shutdown)(struct mmc_host *);
+	int (*hw_reset)(struct mmc_host *, int);
+#define MMC_HW_RESET_RESET	0
+#define MMC_HW_RESET_TEST	1
+#define MMC_HW_RESET_CHECK	2
+#define MMC_HW_RESET_TEST_CARD	3
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 02ad792..7ffa3f2 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1821,6 +1821,45 @@ static int mmc_power_restore(struct mmc_host *host)
 	return ret;
 }
 
+static int mmc_mmc_hw_reset(struct mmc_host *host, int test)
+{
+	struct mmc_card *card = host->card;
+	u8 rst_n_function;
+
+	rst_n_function = card->ext_csd.rst_n_function;
+	if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
+		return -EOPNOTSUPP;
+
+	if (test == MMC_HW_RESET_TEST_CARD)
+		return 0;
+
+	if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
+		return -EOPNOTSUPP;
+
+	if (test == MMC_HW_RESET_TEST)
+		return 0;
+
+	mmc_host_clk_hold(host);
+	mmc_set_clock(host, host->f_init);
+
+	host->ops->hw_reset(host);
+
+	if (test == MMC_HW_RESET_CHECK) {
+		u32 status;
+
+		if (!mmc_send_status(card, &status)) {
+			mmc_host_clk_release(host);
+			return -ENOSYS;
+		}
+	}
+
+	mmc_set_initial_state(host);
+
+	mmc_host_clk_release(host);
+
+	return 0;
+}
+
 static const struct mmc_bus_ops mmc_ops = {
 	.remove = mmc_remove,
 	.detect = mmc_detect,
@@ -1831,6 +1870,7 @@ static const struct mmc_bus_ops mmc_ops = {
 	.power_restore = mmc_power_restore,
 	.alive = mmc_alive,
 	.shutdown = mmc_shutdown,
+	.hw_reset = mmc_mmc_hw_reset,
 };
 
 /*
-- 
1.7.2.5


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

* [PATCH v4 2/2] mmc: sd: add hw_reset callback
  2014-11-24 11:06 [PATCH v4 0/2] mmc: core: hw_reset changes Johan Rudholm
  2014-11-24 11:06 ` [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops Johan Rudholm
@ 2014-11-24 11:06 ` Johan Rudholm
  1 sibling, 0 replies; 9+ messages in thread
From: Johan Rudholm @ 2014-11-24 11:06 UTC (permalink / raw)
  To: linux-mmc, Chris Ball, Ulf Hansson
  Cc: Adrian Hunter, Guennadi Liakhovetski, David Lanzendörfer,
	Jesper Nilsson, Johan Rudholm

Enable power cycle and re-initialization of SD cards via the hw_reset
bus_ops. Power cycling a buggy SD card sometimes helps it get back on
track.

Signed-off-by: Johan Rudholm <johanru@axis.com>
---
 drivers/mmc/core/sd.c |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d90a6de..a7fa124 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1180,6 +1180,18 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
 	return 0;
 }
 
+static int mmc_sd_hw_reset(struct mmc_host *host, int test)
+{
+	struct mmc_card *card = host->card;
+
+	if (test == MMC_HW_RESET_TEST || test == MMC_HW_RESET_TEST_CARD)
+		return 0;
+
+	mmc_power_cycle(host, card->ocr);
+
+	return 0;
+}
+
 static int mmc_sd_power_restore(struct mmc_host *host)
 {
 	int ret;
@@ -1201,6 +1213,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
 	.power_restore = mmc_sd_power_restore,
 	.alive = mmc_sd_alive,
 	.shutdown = mmc_sd_suspend,
+	.hw_reset = mmc_sd_hw_reset,
 };
 
 /*
-- 
1.7.2.5


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

* Re: [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-11-24 11:06 ` [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops Johan Rudholm
@ 2014-11-25 13:48   ` Ulf Hansson
  2014-11-27  9:05     ` Johan Rudholm
  0 siblings, 1 reply; 9+ messages in thread
From: Ulf Hansson @ 2014-11-25 13:48 UTC (permalink / raw)
  To: Johan Rudholm
  Cc: linux-mmc, Chris Ball, Adrian Hunter, Guennadi Liakhovetski,
	David Lanzendörfer, Jesper Nilsson, Johan Rudholm

On 24 November 2014 at 12:06, Johan Rudholm <johan.rudholm@axis.com> wrote:
> Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
> it from the new bus_ops member hw_reset. This also lets us add code
> for resetting SD cards as well.
>
> Signed-off-by: Johan Rudholm <johanru@axis.com>
> ---
>  drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
>  drivers/mmc/core/core.h |    5 ++++
>  drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
>  3 files changed, 63 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 9584bff..492b3e5 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
>         mmc_host_clk_release(host);
>  }
>
> -int mmc_can_reset(struct mmc_card *card)
> -{
> -       u8 rst_n_function;
> -
> -       if (!mmc_card_mmc(card))
> -               return 0;
> -       rst_n_function = card->ext_csd.rst_n_function;
> -       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
> -               return 0;
> -       return 1;
> -}
> -EXPORT_SYMBOL(mmc_can_reset);

Isn't the mmc_can_reset() function used from mmc_test.c?

> -
> +/* Reset card in a bus-specific way */
>  static int mmc_do_hw_reset(struct mmc_host *host, int check)
>  {
> -       struct mmc_card *card = host->card;
> -
> -       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
> -               return -EOPNOTSUPP;
> +       int ret;
>
> -       if (!card)
> +       if (!host->card)
>                 return -EINVAL;
>
> -       if (!mmc_can_reset(card))

You need a mmc_bus_get() here before accessing the host->bus_ops callbacks.

Well, if you would executed this code with the host claimed and from
the mmc block layer, you can be sure the bus_ops to exist. Now, since
this is an exported function, I think you need to be more safe to also
do the mmc_bus_get|put().

> +       if (!host->bus_ops || !host->bus_ops->hw_reset ||
> +                       host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
>                 return -EOPNOTSUPP;
>
> -       mmc_host_clk_hold(host);
> -       mmc_set_clock(host, host->f_init);
> +       ret = host->bus_ops->hw_reset(host, check);

What's going on here? You are invoking the ->hw_reset() callback
twice. That seems odd.

>
> -       host->ops->hw_reset(host);
> -
> -       /* If the reset has happened, then a status command will fail */
> -       if (check) {
> -               u32 status;
> -
> -               if (!mmc_send_status(card, &status)) {
> -                       mmc_host_clk_release(host);
> -                       return -ENOSYS;
> -               }
> -       }
> -
> -       /* Set initial state and call mmc_set_ios */
> -       mmc_set_initial_state(host);
> +       if (check == MMC_HW_RESET_TEST_CARD)
> +               return ret;
>
> -       mmc_host_clk_release(host);
> +       pr_warn("%s: tried to reset card (status %d)\n",
> +                                               mmc_hostname(host), ret);
>
>         return host->bus_ops->power_restore(host);
>  }
>
>  int mmc_hw_reset(struct mmc_host *host)
>  {
> -       return mmc_do_hw_reset(host, 0);
> +       return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
>  }
>  EXPORT_SYMBOL(mmc_hw_reset);
>
>  int mmc_hw_reset_check(struct mmc_host *host)
>  {
> -       return mmc_do_hw_reset(host, 1);
> +       return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
>  }
>  EXPORT_SYMBOL(mmc_hw_reset_check);
>
> +int mmc_can_reset(struct mmc_card *card)
> +{
> +       return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
> +}
> +EXPORT_SYMBOL(mmc_can_reset);
> +
>  static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>  {
>         host->f_init = freq;
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index d76597c..f6e0a52 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -27,6 +27,11 @@ struct mmc_bus_ops {
>         int (*power_restore)(struct mmc_host *);
>         int (*alive)(struct mmc_host *);
>         int (*shutdown)(struct mmc_host *);
> +       int (*hw_reset)(struct mmc_host *, int);
> +#define MMC_HW_RESET_RESET     0
> +#define MMC_HW_RESET_TEST      1
> +#define MMC_HW_RESET_CHECK     2
> +#define MMC_HW_RESET_TEST_CARD 3

Urgh. Is there a way to remove all this? I just don't like all these options.

In fact, I would prefer to have none of them.

>  };
>
>  void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 02ad792..7ffa3f2 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -1821,6 +1821,45 @@ static int mmc_power_restore(struct mmc_host *host)
>         return ret;
>  }
>
> +static int mmc_mmc_hw_reset(struct mmc_host *host, int test)
> +{
> +       struct mmc_card *card = host->card;
> +       u8 rst_n_function;
> +
> +       rst_n_function = card->ext_csd.rst_n_function;
> +       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
> +               return -EOPNOTSUPP;
> +
> +       if (test == MMC_HW_RESET_TEST_CARD)
> +               return 0;
> +
> +       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
> +               return -EOPNOTSUPP;
> +
> +       if (test == MMC_HW_RESET_TEST)
> +               return 0;
> +
> +       mmc_host_clk_hold(host);
> +       mmc_set_clock(host, host->f_init);
> +
> +       host->ops->hw_reset(host);
> +
> +       if (test == MMC_HW_RESET_CHECK) {
> +               u32 status;
> +
> +               if (!mmc_send_status(card, &status)) {
> +                       mmc_host_clk_release(host);
> +                       return -ENOSYS;
> +               }
> +       }
> +
> +       mmc_set_initial_state(host);
> +
> +       mmc_host_clk_release(host);
> +
> +       return 0;
> +}
> +
>  static const struct mmc_bus_ops mmc_ops = {
>         .remove = mmc_remove,
>         .detect = mmc_detect,
> @@ -1831,6 +1870,7 @@ static const struct mmc_bus_ops mmc_ops = {
>         .power_restore = mmc_power_restore,
>         .alive = mmc_alive,
>         .shutdown = mmc_shutdown,
> +       .hw_reset = mmc_mmc_hw_reset,
>  };
>
>  /*
> --
> 1.7.2.5
>

Kind regards
Uffe

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

* Re: [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-11-25 13:48   ` Ulf Hansson
@ 2014-11-27  9:05     ` Johan Rudholm
  2014-11-27  9:50       ` Ulf Hansson
  0 siblings, 1 reply; 9+ messages in thread
From: Johan Rudholm @ 2014-11-27  9:05 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Johan Rudholm, linux-mmc, Chris Ball, Adrian Hunter,
	Guennadi Liakhovetski, David Lanzendörfer, Jesper Nilsson

Hi Ulf,

2014-11-25 14:48 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
> On 24 November 2014 at 12:06, Johan Rudholm <johan.rudholm@axis.com> wrote:
>> Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
>> it from the new bus_ops member hw_reset. This also lets us add code
>> for resetting SD cards as well.
>>
>> Signed-off-by: Johan Rudholm <johanru@axis.com>
>> ---
>>  drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
>>  drivers/mmc/core/core.h |    5 ++++
>>  drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
>>  3 files changed, 63 insertions(+), 38 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 9584bff..492b3e5 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
>>         mmc_host_clk_release(host);
>>  }
>>
>> -int mmc_can_reset(struct mmc_card *card)
>> -{
>> -       u8 rst_n_function;
>> -
>> -       if (!mmc_card_mmc(card))
>> -               return 0;
>> -       rst_n_function = card->ext_csd.rst_n_function;
>> -       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
>> -               return 0;
>> -       return 1;
>> -}
>> -EXPORT_SYMBOL(mmc_can_reset);
>
> Isn't the mmc_can_reset() function used from mmc_test.c?

Yes. Maybe we should move this stuff to mmc_test instead. We could
also move the code that checks if the reset worked or not to mmc_test,
since this is the only place where the check is performed.

>> -
>> +/* Reset card in a bus-specific way */
>>  static int mmc_do_hw_reset(struct mmc_host *host, int check)
>>  {
>> -       struct mmc_card *card = host->card;
>> -
>> -       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
>> -               return -EOPNOTSUPP;
>> +       int ret;
>>
>> -       if (!card)
>> +       if (!host->card)
>>                 return -EINVAL;
>>
>> -       if (!mmc_can_reset(card))
>
> You need a mmc_bus_get() here before accessing the host->bus_ops callbacks.
>
> Well, if you would executed this code with the host claimed and from
> the mmc block layer, you can be sure the bus_ops to exist. Now, since
> this is an exported function, I think you need to be more safe to also
> do the mmc_bus_get|put().

Got it, thanks.

>> +       if (!host->bus_ops || !host->bus_ops->hw_reset ||
>> +                       host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
>>                 return -EOPNOTSUPP;
>>
>> -       mmc_host_clk_hold(host);
>> -       mmc_set_clock(host, host->f_init);
>> +       ret = host->bus_ops->hw_reset(host, check);
>
> What's going on here? You are invoking the ->hw_reset() callback
> twice. That seems odd.
>>
>> -       host->ops->hw_reset(host);
>> -
>> -       /* If the reset has happened, then a status command will fail */
>> -       if (check) {
>> -               u32 status;
>> -
>> -               if (!mmc_send_status(card, &status)) {
>> -                       mmc_host_clk_release(host);
>> -                       return -ENOSYS;
>> -               }
>> -       }
>> -
>> -       /* Set initial state and call mmc_set_ios */
>> -       mmc_set_initial_state(host);
>> +       if (check == MMC_HW_RESET_TEST_CARD)
>> +               return ret;
>>
>> -       mmc_host_clk_release(host);
>> +       pr_warn("%s: tried to reset card (status %d)\n",
>> +                                               mmc_hostname(host), ret);
>>
>>         return host->bus_ops->power_restore(host);
>>  }
>>
>>  int mmc_hw_reset(struct mmc_host *host)
>>  {
>> -       return mmc_do_hw_reset(host, 0);
>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
>>  }
>>  EXPORT_SYMBOL(mmc_hw_reset);
>>
>>  int mmc_hw_reset_check(struct mmc_host *host)
>>  {
>> -       return mmc_do_hw_reset(host, 1);
>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
>>  }
>>  EXPORT_SYMBOL(mmc_hw_reset_check);
>>
>> +int mmc_can_reset(struct mmc_card *card)
>> +{
>> +       return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
>> +}
>> +EXPORT_SYMBOL(mmc_can_reset);
>> +
>>  static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>>  {
>>         host->f_init = freq;
>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>> index d76597c..f6e0a52 100644
>> --- a/drivers/mmc/core/core.h
>> +++ b/drivers/mmc/core/core.h
>> @@ -27,6 +27,11 @@ struct mmc_bus_ops {
>>         int (*power_restore)(struct mmc_host *);
>>         int (*alive)(struct mmc_host *);
>>         int (*shutdown)(struct mmc_host *);
>> +       int (*hw_reset)(struct mmc_host *, int);
>> +#define MMC_HW_RESET_RESET     0
>> +#define MMC_HW_RESET_TEST      1
>> +#define MMC_HW_RESET_CHECK     2
>> +#define MMC_HW_RESET_TEST_CARD 3
>
> Urgh. Is there a way to remove all this? I just don't like all these options.
>
> In fact, I would prefer to have none of them.

If we move the test and check functionality to mmc_test, I think we
can avoid these. What do you think of this approach?

//Johan

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

* Re: [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-11-27  9:05     ` Johan Rudholm
@ 2014-11-27  9:50       ` Ulf Hansson
  2014-11-28 15:54         ` Johan Rudholm
  0 siblings, 1 reply; 9+ messages in thread
From: Ulf Hansson @ 2014-11-27  9:50 UTC (permalink / raw)
  To: Johan Rudholm
  Cc: Johan Rudholm, linux-mmc, Chris Ball, Adrian Hunter,
	Guennadi Liakhovetski, David Lanzendörfer, Jesper Nilsson

On 27 November 2014 at 10:05, Johan Rudholm <johan.rudholm@axis.com> wrote:
> Hi Ulf,
>
> 2014-11-25 14:48 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>> On 24 November 2014 at 12:06, Johan Rudholm <johan.rudholm@axis.com> wrote:
>>> Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
>>> it from the new bus_ops member hw_reset. This also lets us add code
>>> for resetting SD cards as well.
>>>
>>> Signed-off-by: Johan Rudholm <johanru@axis.com>
>>> ---
>>>  drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
>>>  drivers/mmc/core/core.h |    5 ++++
>>>  drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
>>>  3 files changed, 63 insertions(+), 38 deletions(-)
>>>
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 9584bff..492b3e5 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
>>>         mmc_host_clk_release(host);
>>>  }
>>>
>>> -int mmc_can_reset(struct mmc_card *card)
>>> -{
>>> -       u8 rst_n_function;
>>> -
>>> -       if (!mmc_card_mmc(card))
>>> -               return 0;
>>> -       rst_n_function = card->ext_csd.rst_n_function;
>>> -       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
>>> -               return 0;
>>> -       return 1;
>>> -}
>>> -EXPORT_SYMBOL(mmc_can_reset);
>>
>> Isn't the mmc_can_reset() function used from mmc_test.c?
>
> Yes. Maybe we should move this stuff to mmc_test instead. We could
> also move the code that checks if the reset worked or not to mmc_test,
> since this is the only place where the check is performed.
>
>>> -
>>> +/* Reset card in a bus-specific way */
>>>  static int mmc_do_hw_reset(struct mmc_host *host, int check)
>>>  {
>>> -       struct mmc_card *card = host->card;
>>> -
>>> -       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
>>> -               return -EOPNOTSUPP;
>>> +       int ret;
>>>
>>> -       if (!card)
>>> +       if (!host->card)
>>>                 return -EINVAL;
>>>
>>> -       if (!mmc_can_reset(card))
>>
>> You need a mmc_bus_get() here before accessing the host->bus_ops callbacks.
>>
>> Well, if you would executed this code with the host claimed and from
>> the mmc block layer, you can be sure the bus_ops to exist. Now, since
>> this is an exported function, I think you need to be more safe to also
>> do the mmc_bus_get|put().
>
> Got it, thanks.
>
>>> +       if (!host->bus_ops || !host->bus_ops->hw_reset ||
>>> +                       host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
>>>                 return -EOPNOTSUPP;
>>>
>>> -       mmc_host_clk_hold(host);
>>> -       mmc_set_clock(host, host->f_init);
>>> +       ret = host->bus_ops->hw_reset(host, check);
>>
>> What's going on here? You are invoking the ->hw_reset() callback
>> twice. That seems odd.
>>>
>>> -       host->ops->hw_reset(host);
>>> -
>>> -       /* If the reset has happened, then a status command will fail */
>>> -       if (check) {
>>> -               u32 status;
>>> -
>>> -               if (!mmc_send_status(card, &status)) {
>>> -                       mmc_host_clk_release(host);
>>> -                       return -ENOSYS;
>>> -               }
>>> -       }
>>> -
>>> -       /* Set initial state and call mmc_set_ios */
>>> -       mmc_set_initial_state(host);
>>> +       if (check == MMC_HW_RESET_TEST_CARD)
>>> +               return ret;
>>>
>>> -       mmc_host_clk_release(host);
>>> +       pr_warn("%s: tried to reset card (status %d)\n",
>>> +                                               mmc_hostname(host), ret);
>>>
>>>         return host->bus_ops->power_restore(host);
>>>  }
>>>
>>>  int mmc_hw_reset(struct mmc_host *host)
>>>  {
>>> -       return mmc_do_hw_reset(host, 0);
>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
>>>  }
>>>  EXPORT_SYMBOL(mmc_hw_reset);
>>>
>>>  int mmc_hw_reset_check(struct mmc_host *host)
>>>  {
>>> -       return mmc_do_hw_reset(host, 1);
>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
>>>  }
>>>  EXPORT_SYMBOL(mmc_hw_reset_check);
>>>
>>> +int mmc_can_reset(struct mmc_card *card)
>>> +{
>>> +       return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
>>> +}
>>> +EXPORT_SYMBOL(mmc_can_reset);
>>> +
>>>  static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>>>  {
>>>         host->f_init = freq;
>>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>>> index d76597c..f6e0a52 100644
>>> --- a/drivers/mmc/core/core.h
>>> +++ b/drivers/mmc/core/core.h
>>> @@ -27,6 +27,11 @@ struct mmc_bus_ops {
>>>         int (*power_restore)(struct mmc_host *);
>>>         int (*alive)(struct mmc_host *);
>>>         int (*shutdown)(struct mmc_host *);
>>> +       int (*hw_reset)(struct mmc_host *, int);
>>> +#define MMC_HW_RESET_RESET     0
>>> +#define MMC_HW_RESET_TEST      1
>>> +#define MMC_HW_RESET_CHECK     2
>>> +#define MMC_HW_RESET_TEST_CARD 3
>>
>> Urgh. Is there a way to remove all this? I just don't like all these options.
>>
>> In fact, I would prefer to have none of them.
>
> If we move the test and check functionality to mmc_test, I think we
> can avoid these. What do you think of this approach?

I like that approach.

In principle I think we should have only one API for hardware reset,
typically that should be mmc_hw_reset(). Then, convert
mmc_test_hw_reset() into invoking the mmc_hw_reset() API and let it
handle further tests by itself.

Kind regards
Uffe

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

* Re: [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-11-27  9:50       ` Ulf Hansson
@ 2014-11-28 15:54         ` Johan Rudholm
  2014-12-01 10:50           ` Ulf Hansson
  0 siblings, 1 reply; 9+ messages in thread
From: Johan Rudholm @ 2014-11-28 15:54 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Johan Rudholm, linux-mmc, Chris Ball, Adrian Hunter,
	Guennadi Liakhovetski, David Lanzendörfer, Jesper Nilsson

2014-11-27 10:50 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
> On 27 November 2014 at 10:05, Johan Rudholm <johan.rudholm@axis.com> wrote:
>> Hi Ulf,
>>
>> 2014-11-25 14:48 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>> On 24 November 2014 at 12:06, Johan Rudholm <johan.rudholm@axis.com> wrote:
>>>> Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
>>>> it from the new bus_ops member hw_reset. This also lets us add code
>>>> for resetting SD cards as well.
>>>>
>>>> Signed-off-by: Johan Rudholm <johanru@axis.com>
>>>> ---
>>>>  drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
>>>>  drivers/mmc/core/core.h |    5 ++++
>>>>  drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
>>>>  3 files changed, 63 insertions(+), 38 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>> index 9584bff..492b3e5 100644
>>>> --- a/drivers/mmc/core/core.c
>>>> +++ b/drivers/mmc/core/core.c
>>>> @@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
>>>>         mmc_host_clk_release(host);
>>>>  }
>>>>
>>>> -int mmc_can_reset(struct mmc_card *card)
>>>> -{
>>>> -       u8 rst_n_function;
>>>> -
>>>> -       if (!mmc_card_mmc(card))
>>>> -               return 0;
>>>> -       rst_n_function = card->ext_csd.rst_n_function;
>>>> -       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
>>>> -               return 0;
>>>> -       return 1;
>>>> -}
>>>> -EXPORT_SYMBOL(mmc_can_reset);
>>>
>>> Isn't the mmc_can_reset() function used from mmc_test.c?
>>
>> Yes. Maybe we should move this stuff to mmc_test instead. We could
>> also move the code that checks if the reset worked or not to mmc_test,
>> since this is the only place where the check is performed.
>>
>>>> -
>>>> +/* Reset card in a bus-specific way */
>>>>  static int mmc_do_hw_reset(struct mmc_host *host, int check)
>>>>  {
>>>> -       struct mmc_card *card = host->card;
>>>> -
>>>> -       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
>>>> -               return -EOPNOTSUPP;
>>>> +       int ret;
>>>>
>>>> -       if (!card)
>>>> +       if (!host->card)
>>>>                 return -EINVAL;
>>>>
>>>> -       if (!mmc_can_reset(card))
>>>
>>> You need a mmc_bus_get() here before accessing the host->bus_ops callbacks.
>>>
>>> Well, if you would executed this code with the host claimed and from
>>> the mmc block layer, you can be sure the bus_ops to exist. Now, since
>>> this is an exported function, I think you need to be more safe to also
>>> do the mmc_bus_get|put().
>>
>> Got it, thanks.
>>
>>>> +       if (!host->bus_ops || !host->bus_ops->hw_reset ||
>>>> +                       host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
>>>>                 return -EOPNOTSUPP;
>>>>
>>>> -       mmc_host_clk_hold(host);
>>>> -       mmc_set_clock(host, host->f_init);
>>>> +       ret = host->bus_ops->hw_reset(host, check);
>>>
>>> What's going on here? You are invoking the ->hw_reset() callback
>>> twice. That seems odd.
>>>>
>>>> -       host->ops->hw_reset(host);
>>>> -
>>>> -       /* If the reset has happened, then a status command will fail */
>>>> -       if (check) {
>>>> -               u32 status;
>>>> -
>>>> -               if (!mmc_send_status(card, &status)) {
>>>> -                       mmc_host_clk_release(host);
>>>> -                       return -ENOSYS;
>>>> -               }
>>>> -       }
>>>> -
>>>> -       /* Set initial state and call mmc_set_ios */
>>>> -       mmc_set_initial_state(host);
>>>> +       if (check == MMC_HW_RESET_TEST_CARD)
>>>> +               return ret;
>>>>
>>>> -       mmc_host_clk_release(host);
>>>> +       pr_warn("%s: tried to reset card (status %d)\n",
>>>> +                                               mmc_hostname(host), ret);
>>>>
>>>>         return host->bus_ops->power_restore(host);
>>>>  }
>>>>
>>>>  int mmc_hw_reset(struct mmc_host *host)
>>>>  {
>>>> -       return mmc_do_hw_reset(host, 0);
>>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
>>>>  }
>>>>  EXPORT_SYMBOL(mmc_hw_reset);
>>>>
>>>>  int mmc_hw_reset_check(struct mmc_host *host)
>>>>  {
>>>> -       return mmc_do_hw_reset(host, 1);
>>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
>>>>  }
>>>>  EXPORT_SYMBOL(mmc_hw_reset_check);
>>>>
>>>> +int mmc_can_reset(struct mmc_card *card)
>>>> +{
>>>> +       return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
>>>> +}
>>>> +EXPORT_SYMBOL(mmc_can_reset);
>>>> +
>>>>  static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>>>>  {
>>>>         host->f_init = freq;
>>>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>>>> index d76597c..f6e0a52 100644
>>>> --- a/drivers/mmc/core/core.h
>>>> +++ b/drivers/mmc/core/core.h
>>>> @@ -27,6 +27,11 @@ struct mmc_bus_ops {
>>>>         int (*power_restore)(struct mmc_host *);
>>>>         int (*alive)(struct mmc_host *);
>>>>         int (*shutdown)(struct mmc_host *);
>>>> +       int (*hw_reset)(struct mmc_host *, int);
>>>> +#define MMC_HW_RESET_RESET     0
>>>> +#define MMC_HW_RESET_TEST      1
>>>> +#define MMC_HW_RESET_CHECK     2
>>>> +#define MMC_HW_RESET_TEST_CARD 3
>>>
>>> Urgh. Is there a way to remove all this? I just don't like all these options.
>>>
>>> In fact, I would prefer to have none of them.
>>
>> If we move the test and check functionality to mmc_test, I think we
>> can avoid these. What do you think of this approach?
>
> I like that approach.
>
> In principle I think we should have only one API for hardware reset,
> typically that should be mmc_hw_reset(). Then, convert
> mmc_test_hw_reset() into invoking the mmc_hw_reset() API and let it
> handle further tests by itself.

The trouble is that mmc_test_hw_reset() needs to check if the reset
was a success after calling host_ops->hw_reset, but before calling
mmc_set_initial_values and bus_ops->power_restore, so some provisions
for this need to be done in mmc_hw_reset. I'll soon send up a new
patchset, please let me know if I'm on the right track.

BR, Johan

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

* Re: [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-11-28 15:54         ` Johan Rudholm
@ 2014-12-01 10:50           ` Ulf Hansson
  2015-01-09 11:06             ` Johan Rudholm
  0 siblings, 1 reply; 9+ messages in thread
From: Ulf Hansson @ 2014-12-01 10:50 UTC (permalink / raw)
  To: Johan Rudholm
  Cc: Johan Rudholm, linux-mmc, Chris Ball, Adrian Hunter,
	Guennadi Liakhovetski, David Lanzendörfer, Jesper Nilsson

On 28 November 2014 at 16:54, Johan Rudholm <johan.rudholm@axis.com> wrote:
> 2014-11-27 10:50 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>> On 27 November 2014 at 10:05, Johan Rudholm <johan.rudholm@axis.com> wrote:
>>> Hi Ulf,
>>>
>>> 2014-11-25 14:48 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>>> On 24 November 2014 at 12:06, Johan Rudholm <johan.rudholm@axis.com> wrote:
>>>>> Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
>>>>> it from the new bus_ops member hw_reset. This also lets us add code
>>>>> for resetting SD cards as well.
>>>>>
>>>>> Signed-off-by: Johan Rudholm <johanru@axis.com>
>>>>> ---
>>>>>  drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
>>>>>  drivers/mmc/core/core.h |    5 ++++
>>>>>  drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
>>>>>  3 files changed, 63 insertions(+), 38 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>> index 9584bff..492b3e5 100644
>>>>> --- a/drivers/mmc/core/core.c
>>>>> +++ b/drivers/mmc/core/core.c
>>>>> @@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
>>>>>         mmc_host_clk_release(host);
>>>>>  }
>>>>>
>>>>> -int mmc_can_reset(struct mmc_card *card)
>>>>> -{
>>>>> -       u8 rst_n_function;
>>>>> -
>>>>> -       if (!mmc_card_mmc(card))
>>>>> -               return 0;
>>>>> -       rst_n_function = card->ext_csd.rst_n_function;
>>>>> -       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
>>>>> -               return 0;
>>>>> -       return 1;
>>>>> -}
>>>>> -EXPORT_SYMBOL(mmc_can_reset);
>>>>
>>>> Isn't the mmc_can_reset() function used from mmc_test.c?
>>>
>>> Yes. Maybe we should move this stuff to mmc_test instead. We could
>>> also move the code that checks if the reset worked or not to mmc_test,
>>> since this is the only place where the check is performed.
>>>
>>>>> -
>>>>> +/* Reset card in a bus-specific way */
>>>>>  static int mmc_do_hw_reset(struct mmc_host *host, int check)
>>>>>  {
>>>>> -       struct mmc_card *card = host->card;
>>>>> -
>>>>> -       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
>>>>> -               return -EOPNOTSUPP;
>>>>> +       int ret;
>>>>>
>>>>> -       if (!card)
>>>>> +       if (!host->card)
>>>>>                 return -EINVAL;
>>>>>
>>>>> -       if (!mmc_can_reset(card))
>>>>
>>>> You need a mmc_bus_get() here before accessing the host->bus_ops callbacks.
>>>>
>>>> Well, if you would executed this code with the host claimed and from
>>>> the mmc block layer, you can be sure the bus_ops to exist. Now, since
>>>> this is an exported function, I think you need to be more safe to also
>>>> do the mmc_bus_get|put().
>>>
>>> Got it, thanks.
>>>
>>>>> +       if (!host->bus_ops || !host->bus_ops->hw_reset ||
>>>>> +                       host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
>>>>>                 return -EOPNOTSUPP;
>>>>>
>>>>> -       mmc_host_clk_hold(host);
>>>>> -       mmc_set_clock(host, host->f_init);
>>>>> +       ret = host->bus_ops->hw_reset(host, check);
>>>>
>>>> What's going on here? You are invoking the ->hw_reset() callback
>>>> twice. That seems odd.
>>>>>
>>>>> -       host->ops->hw_reset(host);
>>>>> -
>>>>> -       /* If the reset has happened, then a status command will fail */
>>>>> -       if (check) {
>>>>> -               u32 status;
>>>>> -
>>>>> -               if (!mmc_send_status(card, &status)) {
>>>>> -                       mmc_host_clk_release(host);
>>>>> -                       return -ENOSYS;
>>>>> -               }
>>>>> -       }
>>>>> -
>>>>> -       /* Set initial state and call mmc_set_ios */
>>>>> -       mmc_set_initial_state(host);
>>>>> +       if (check == MMC_HW_RESET_TEST_CARD)
>>>>> +               return ret;
>>>>>
>>>>> -       mmc_host_clk_release(host);
>>>>> +       pr_warn("%s: tried to reset card (status %d)\n",
>>>>> +                                               mmc_hostname(host), ret);
>>>>>
>>>>>         return host->bus_ops->power_restore(host);
>>>>>  }
>>>>>
>>>>>  int mmc_hw_reset(struct mmc_host *host)
>>>>>  {
>>>>> -       return mmc_do_hw_reset(host, 0);
>>>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
>>>>>  }
>>>>>  EXPORT_SYMBOL(mmc_hw_reset);
>>>>>
>>>>>  int mmc_hw_reset_check(struct mmc_host *host)
>>>>>  {
>>>>> -       return mmc_do_hw_reset(host, 1);
>>>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
>>>>>  }
>>>>>  EXPORT_SYMBOL(mmc_hw_reset_check);
>>>>>
>>>>> +int mmc_can_reset(struct mmc_card *card)
>>>>> +{
>>>>> +       return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
>>>>> +}
>>>>> +EXPORT_SYMBOL(mmc_can_reset);
>>>>> +
>>>>>  static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>>>>>  {
>>>>>         host->f_init = freq;
>>>>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>>>>> index d76597c..f6e0a52 100644
>>>>> --- a/drivers/mmc/core/core.h
>>>>> +++ b/drivers/mmc/core/core.h
>>>>> @@ -27,6 +27,11 @@ struct mmc_bus_ops {
>>>>>         int (*power_restore)(struct mmc_host *);
>>>>>         int (*alive)(struct mmc_host *);
>>>>>         int (*shutdown)(struct mmc_host *);
>>>>> +       int (*hw_reset)(struct mmc_host *, int);
>>>>> +#define MMC_HW_RESET_RESET     0
>>>>> +#define MMC_HW_RESET_TEST      1
>>>>> +#define MMC_HW_RESET_CHECK     2
>>>>> +#define MMC_HW_RESET_TEST_CARD 3
>>>>
>>>> Urgh. Is there a way to remove all this? I just don't like all these options.
>>>>
>>>> In fact, I would prefer to have none of them.
>>>
>>> If we move the test and check functionality to mmc_test, I think we
>>> can avoid these. What do you think of this approach?
>>
>> I like that approach.
>>
>> In principle I think we should have only one API for hardware reset,
>> typically that should be mmc_hw_reset(). Then, convert
>> mmc_test_hw_reset() into invoking the mmc_hw_reset() API and let it
>> handle further tests by itself.
>
> The trouble is that mmc_test_hw_reset() needs to check if the reset
> was a success after calling host_ops->hw_reset, but before calling

How about always do the CMD13 check when host_ops->hw_reset() exist
and returns suceess? The error handling for CMD13 check could be print
an error. It shouldn't prevent us from proceeding with the rest of
power cycle operations.

> mmc_set_initial_values and bus_ops->power_restore, so some provisions
> for this need to be done in mmc_hw_reset. I'll soon send up a new
> patchset, please let me know if I'm on the right track.
>

Kind regards
Uffe

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

* Re: [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops
  2014-12-01 10:50           ` Ulf Hansson
@ 2015-01-09 11:06             ` Johan Rudholm
  0 siblings, 0 replies; 9+ messages in thread
From: Johan Rudholm @ 2015-01-09 11:06 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Johan Rudholm, linux-mmc, Chris Ball, Adrian Hunter,
	Guennadi Liakhovetski, David Lanzendörfer, Jesper Nilsson

Hi Ulf,

2014-12-01 11:50 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
> On 28 November 2014 at 16:54, Johan Rudholm <johan.rudholm@axis.com> wrote:
>> 2014-11-27 10:50 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>> On 27 November 2014 at 10:05, Johan Rudholm <johan.rudholm@axis.com> wrote:
>>>> Hi Ulf,
>>>>
>>>> 2014-11-25 14:48 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>>>> On 24 November 2014 at 12:06, Johan Rudholm <johan.rudholm@axis.com> wrote:
>>>>>> Move the (e)MMC specific hw_reset code from core.c into mmc.c and call
>>>>>> it from the new bus_ops member hw_reset. This also lets us add code
>>>>>> for resetting SD cards as well.
>>>>>>
>>>>>> Signed-off-by: Johan Rudholm <johanru@axis.com>
>>>>>> ---
>>>>>>  drivers/mmc/core/core.c |   56 +++++++++++++++-------------------------------
>>>>>>  drivers/mmc/core/core.h |    5 ++++
>>>>>>  drivers/mmc/core/mmc.c  |   40 +++++++++++++++++++++++++++++++++
>>>>>>  3 files changed, 63 insertions(+), 38 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>> index 9584bff..492b3e5 100644
>>>>>> --- a/drivers/mmc/core/core.c
>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>> @@ -2245,67 +2245,47 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
>>>>>>         mmc_host_clk_release(host);
>>>>>>  }
>>>>>>
>>>>>> -int mmc_can_reset(struct mmc_card *card)
>>>>>> -{
>>>>>> -       u8 rst_n_function;
>>>>>> -
>>>>>> -       if (!mmc_card_mmc(card))
>>>>>> -               return 0;
>>>>>> -       rst_n_function = card->ext_csd.rst_n_function;
>>>>>> -       if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
>>>>>> -               return 0;
>>>>>> -       return 1;
>>>>>> -}
>>>>>> -EXPORT_SYMBOL(mmc_can_reset);
>>>>>
>>>>> Isn't the mmc_can_reset() function used from mmc_test.c?
>>>>
>>>> Yes. Maybe we should move this stuff to mmc_test instead. We could
>>>> also move the code that checks if the reset worked or not to mmc_test,
>>>> since this is the only place where the check is performed.
>>>>
>>>>>> -
>>>>>> +/* Reset card in a bus-specific way */
>>>>>>  static int mmc_do_hw_reset(struct mmc_host *host, int check)
>>>>>>  {
>>>>>> -       struct mmc_card *card = host->card;
>>>>>> -
>>>>>> -       if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
>>>>>> -               return -EOPNOTSUPP;
>>>>>> +       int ret;
>>>>>>
>>>>>> -       if (!card)
>>>>>> +       if (!host->card)
>>>>>>                 return -EINVAL;
>>>>>>
>>>>>> -       if (!mmc_can_reset(card))
>>>>>
>>>>> You need a mmc_bus_get() here before accessing the host->bus_ops callbacks.
>>>>>
>>>>> Well, if you would executed this code with the host claimed and from
>>>>> the mmc block layer, you can be sure the bus_ops to exist. Now, since
>>>>> this is an exported function, I think you need to be more safe to also
>>>>> do the mmc_bus_get|put().
>>>>
>>>> Got it, thanks.
>>>>
>>>>>> +       if (!host->bus_ops || !host->bus_ops->hw_reset ||
>>>>>> +                       host->bus_ops->hw_reset(host, MMC_HW_RESET_TEST))
>>>>>>                 return -EOPNOTSUPP;
>>>>>>
>>>>>> -       mmc_host_clk_hold(host);
>>>>>> -       mmc_set_clock(host, host->f_init);
>>>>>> +       ret = host->bus_ops->hw_reset(host, check);
>>>>>
>>>>> What's going on here? You are invoking the ->hw_reset() callback
>>>>> twice. That seems odd.
>>>>>>
>>>>>> -       host->ops->hw_reset(host);
>>>>>> -
>>>>>> -       /* If the reset has happened, then a status command will fail */
>>>>>> -       if (check) {
>>>>>> -               u32 status;
>>>>>> -
>>>>>> -               if (!mmc_send_status(card, &status)) {
>>>>>> -                       mmc_host_clk_release(host);
>>>>>> -                       return -ENOSYS;
>>>>>> -               }
>>>>>> -       }
>>>>>> -
>>>>>> -       /* Set initial state and call mmc_set_ios */
>>>>>> -       mmc_set_initial_state(host);
>>>>>> +       if (check == MMC_HW_RESET_TEST_CARD)
>>>>>> +               return ret;
>>>>>>
>>>>>> -       mmc_host_clk_release(host);
>>>>>> +       pr_warn("%s: tried to reset card (status %d)\n",
>>>>>> +                                               mmc_hostname(host), ret);
>>>>>>
>>>>>>         return host->bus_ops->power_restore(host);
>>>>>>  }
>>>>>>
>>>>>>  int mmc_hw_reset(struct mmc_host *host)
>>>>>>  {
>>>>>> -       return mmc_do_hw_reset(host, 0);
>>>>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_RESET);
>>>>>>  }
>>>>>>  EXPORT_SYMBOL(mmc_hw_reset);
>>>>>>
>>>>>>  int mmc_hw_reset_check(struct mmc_host *host)
>>>>>>  {
>>>>>> -       return mmc_do_hw_reset(host, 1);
>>>>>> +       return mmc_do_hw_reset(host, MMC_HW_RESET_CHECK);
>>>>>>  }
>>>>>>  EXPORT_SYMBOL(mmc_hw_reset_check);
>>>>>>
>>>>>> +int mmc_can_reset(struct mmc_card *card)
>>>>>> +{
>>>>>> +       return mmc_do_hw_reset(card->host, MMC_HW_RESET_TEST_CARD);
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(mmc_can_reset);
>>>>>> +
>>>>>>  static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>>>>>>  {
>>>>>>         host->f_init = freq;
>>>>>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>>>>>> index d76597c..f6e0a52 100644
>>>>>> --- a/drivers/mmc/core/core.h
>>>>>> +++ b/drivers/mmc/core/core.h
>>>>>> @@ -27,6 +27,11 @@ struct mmc_bus_ops {
>>>>>>         int (*power_restore)(struct mmc_host *);
>>>>>>         int (*alive)(struct mmc_host *);
>>>>>>         int (*shutdown)(struct mmc_host *);
>>>>>> +       int (*hw_reset)(struct mmc_host *, int);
>>>>>> +#define MMC_HW_RESET_RESET     0
>>>>>> +#define MMC_HW_RESET_TEST      1
>>>>>> +#define MMC_HW_RESET_CHECK     2
>>>>>> +#define MMC_HW_RESET_TEST_CARD 3
>>>>>
>>>>> Urgh. Is there a way to remove all this? I just don't like all these options.
>>>>>
>>>>> In fact, I would prefer to have none of them.
>>>>
>>>> If we move the test and check functionality to mmc_test, I think we
>>>> can avoid these. What do you think of this approach?
>>>
>>> I like that approach.
>>>
>>> In principle I think we should have only one API for hardware reset,
>>> typically that should be mmc_hw_reset(). Then, convert
>>> mmc_test_hw_reset() into invoking the mmc_hw_reset() API and let it
>>> handle further tests by itself.
>>
>> The trouble is that mmc_test_hw_reset() needs to check if the reset
>> was a success after calling host_ops->hw_reset, but before calling
>
> How about always do the CMD13 check when host_ops->hw_reset() exist
> and returns suceess? The error handling for CMD13 check could be print
> an error. It shouldn't prevent us from proceeding with the rest of
> power cycle operations.

Sorry for having dropped this conversation for so long. I've thought
about the best approach for a while now and I'll soon send out a patch
set with your suggestions incorporated.

Kind regards, Johan

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

end of thread, other threads:[~2015-01-09 11:06 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-24 11:06 [PATCH v4 0/2] mmc: core: hw_reset changes Johan Rudholm
2014-11-24 11:06 ` [PATCH v4 1/2] mmc: core: turn hw_reset into a bus_ops Johan Rudholm
2014-11-25 13:48   ` Ulf Hansson
2014-11-27  9:05     ` Johan Rudholm
2014-11-27  9:50       ` Ulf Hansson
2014-11-28 15:54         ` Johan Rudholm
2014-12-01 10:50           ` Ulf Hansson
2015-01-09 11:06             ` Johan Rudholm
2014-11-24 11:06 ` [PATCH v4 2/2] mmc: sd: add hw_reset callback Johan Rudholm

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.