* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox