Commit 002ee28e authored by Andrea Merello's avatar Andrea Merello Committed by Ulf Hansson

mmc: core: make pwrseq_emmc (partially) support sleepy GPIO controllers

pwrseq_emmc.c implements a HW reset procedure for eMMC chip by driving a
GPIO line.

It registers the .reset() cb on mmc_pwrseq_ops and it registers a system
restart notification handler; both of them perform reset by unconditionally
calling gpiod_set_value().

If the eMMC reset line is tied to a GPIO controller whose driver can sleep
(i.e. I2C GPIO controller), then the kernel would spit warnings when trying
to reset the eMMC chip by means of .reset() mmc_pwrseq_ops cb (that is
exactly what I'm seeing during boot).

Furthermore, on system reset we would gets to the system restart
notification handler with disabled interrupts - local_irq_disable() is
called in machine_restart() at least on ARM/ARM64 - and we would be in
trouble when the GPIO driver tries to sleep (which indeed doesn't happen
here, likely because in my case the machine specific code doesn't call
do_kernel_restart(), I guess..).

This patch fixes the .reset() cb to make use of gpiod_set_value_cansleep(),
so that the eMMC gets reset on boot without complaints, while, since there
isn't that much we can do, we avoid register the restart handler if the
GPIO controller has a sleepy driver (and we spit a dev_notice() message to
let people know)..

This had been tested on a downstream 4.9 kernel with backported
commit 83f37ee7ba33 ("mmc: pwrseq: Add reset callback to the struct
mmc_pwrseq_ops") and commit ae60fb031cf2 ("mmc: core: Don't do eMMC HW
reset when resuming the eMMC card"), because I couldn't boot my board
otherwise. Maybe worth to RFT.
Signed-off-by: default avatarAndrea Merello <andrea.merello@gmail.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent b65be635
...@@ -30,19 +30,14 @@ struct mmc_pwrseq_emmc { ...@@ -30,19 +30,14 @@ struct mmc_pwrseq_emmc {
#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq) #define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq)
static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
{
gpiod_set_value(pwrseq->reset_gpio, 1);
udelay(1);
gpiod_set_value(pwrseq->reset_gpio, 0);
udelay(200);
}
static void mmc_pwrseq_emmc_reset(struct mmc_host *host) static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
{ {
struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq);
__mmc_pwrseq_emmc_reset(pwrseq); gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
udelay(1);
gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
udelay(200);
} }
static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
...@@ -50,8 +45,11 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, ...@@ -50,8 +45,11 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
{ {
struct mmc_pwrseq_emmc *pwrseq = container_of(this, struct mmc_pwrseq_emmc *pwrseq = container_of(this,
struct mmc_pwrseq_emmc, reset_nb); struct mmc_pwrseq_emmc, reset_nb);
gpiod_set_value(pwrseq->reset_gpio, 1);
udelay(1);
gpiod_set_value(pwrseq->reset_gpio, 0);
udelay(200);
__mmc_pwrseq_emmc_reset(pwrseq);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -72,6 +70,7 @@ static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) ...@@ -72,6 +70,7 @@ static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
if (IS_ERR(pwrseq->reset_gpio)) if (IS_ERR(pwrseq->reset_gpio))
return PTR_ERR(pwrseq->reset_gpio); return PTR_ERR(pwrseq->reset_gpio);
if (!gpiod_cansleep(pwrseq->reset_gpio)) {
/* /*
* register reset handler to ensure emmc reset also from * register reset handler to ensure emmc reset also from
* emergency_reboot(), priority 255 is the highest priority * emergency_reboot(), priority 255 is the highest priority
...@@ -80,6 +79,9 @@ static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) ...@@ -80,6 +79,9 @@ static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
pwrseq->reset_nb.priority = 255; pwrseq->reset_nb.priority = 255;
register_restart_handler(&pwrseq->reset_nb); register_restart_handler(&pwrseq->reset_nb);
} else {
dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n");
}
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
pwrseq->pwrseq.dev = dev; pwrseq->pwrseq.dev = dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment