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: Andrea Merello <andrea.merello@gmail.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
b65be63551
commit
002ee28e8b
|
@ -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,
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue