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)
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
|
@ -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, 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;
|
||||
}
|
||||
|
||||
|
@ -72,14 +70,18 @@ static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(pwrseq->reset_gpio))
|
||||
return PTR_ERR(pwrseq->reset_gpio);
|
||||
|
||||
/*
|
||||
* register reset handler to ensure emmc reset also from
|
||||
* emergency_reboot(), priority 255 is the highest priority
|
||||
* so it will be executed before any system reboot handler.
|
||||
*/
|
||||
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
|
||||
pwrseq->reset_nb.priority = 255;
|
||||
register_restart_handler(&pwrseq->reset_nb);
|
||||
if (!gpiod_cansleep(pwrseq->reset_gpio)) {
|
||||
/*
|
||||
* register reset handler to ensure emmc reset also from
|
||||
* emergency_reboot(), priority 255 is the highest priority
|
||||
* so it will be executed before any system reboot handler.
|
||||
*/
|
||||
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
|
||||
pwrseq->reset_nb.priority = 255;
|
||||
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.dev = dev;
|
||||
|
|
Loading…
Reference in New Issue