lkml.org 
[lkml]   [2019]   [May]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.14 117/193] mmc: core: make pwrseq_emmc (partially) support sleepy GPIO controllers
    Date
    [ Upstream commit 002ee28e8b322d4d4b7b83234b5d0f4ebd428eda ]

    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>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    drivers/mmc/core/pwrseq_emmc.c | 38 ++++++++++++++++++----------------
    1 file changed, 20 insertions(+), 18 deletions(-)

    diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
    index efb8a7965dd4a..154f4204d58cb 100644
    --- a/drivers/mmc/core/pwrseq_emmc.c
    +++ b/drivers/mmc/core/pwrseq_emmc.c
    @@ -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;
    --
    2.20.1


    \
     
     \ /
      Last update: 2019-05-30 05:54    [W:3.140 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site