All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ulf Hansson <ulf.hansson@linaro.org>
To: Chaotian Jing <chaotian.jing@mediatek.com>
Cc: "Rob Herring" <robh+dt@kernel.org>,
	"Matthias Brugger" <matthias.bgg@gmail.com>,
	"Chris Ball" <chris@printf.net>,
	"Mark Rutland" <mark.rutland@arm.com>,
	"James Liao" <jamesjj.liao@mediatek.com>,
	srv_heupstream <srv_heupstream@mediatek.com>,
	"Arnd Bergmann" <arnd@arndb.de>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"Hongzhou Yang" <hongzhou.yang@mediatek.com>,
	"Catalin Marinas" <catalin.marinas@arm.com>,
	linux-mmc <linux-mmc@vger.kernel.org>,
	"Will Deacon" <will.deacon@arm.com>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"linux-gpio@vger.kernel.org" <linux-gpio@vger.kernel.org>,
	"Sascha Hauer" <kernel@pengutronix.de>,
	"Joe.C" <yingjoe.chen@mediatek.com>,
	"Eddie Huang" <eddie.huang@mediatek.com>,
	"Bin Zhang (章斌)" <bin.zhang@mediatek.com>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>
Subject: Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
Date: Mon, 17 Aug 2015 13:31:58 +0200	[thread overview]
Message-ID: <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ@mail.gmail.com> (raw)
In-Reply-To: <1439367845-5891-2-git-send-email-chaotian.jing@mediatek.com>

On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
> Schedule a workqueue to do tuning when CRC error
> Call mmc_hw_reset to re-init card when data timeout

Thanks to Adrian Hunter, the mmc core already supports re-tuning for
the above scenarios through the mmc_retune_*() APIs.

SDHCI driver has already adopted to use that feature, you should do
that for the mtk-sd driver as well.

Kind regards
Uffe

>
> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> ---
>  drivers/mmc/host/mtk-sd.c | 162 ++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 150 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 7153500..eb44fe1 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -27,6 +27,7 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/spinlock.h>
> +#include <linux/workqueue.h>
>
>  #include <linux/mmc/card.h>
>  #include <linux/mmc/core.h>
> @@ -290,6 +291,10 @@ struct msdc_host {
>         struct pinctrl_state *pins_default;
>         struct pinctrl_state *pins_uhs;
>         struct delayed_work req_timeout;
> +       struct workqueue_struct *repeat_workqueue;
> +       struct work_struct repeat_req;
> +       struct mmc_request *repeat_mrq;
> +       u32 tune_cmd_counter;
>         int irq;                /* host interrupt */
>
>         struct clk *src_clk;    /* msdc source clock */
> @@ -353,7 +358,10 @@ static void msdc_reset_hw(struct msdc_host *host)
>  static void msdc_cmd_next(struct msdc_host *host,
>                 struct mmc_request *mrq, struct mmc_command *cmd);
>
> -static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
> +static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
> +                       MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
> +                       MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
> +static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
>                         MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
>                         MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
>
> @@ -690,6 +698,18 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
>         msdc_track_cmd_data(host, mrq->cmd, mrq->data);
>         if (mrq->data)
>                 msdc_unprepare_data(host, mrq);
> +       if (host->error && host->mmc->card &&
> +           !mmc_card_sdio(host->mmc->card)) {
> +               if (mrq->cmd->error == (unsigned int)-EILSEQ ||
> +                   (mrq->stop && mrq->stop->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->sbc && mrq->sbc->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->data && mrq->data->error))  {
> +                       host->repeat_mrq = mrq;
> +                       queue_work(host->repeat_workqueue, &host->repeat_req);
> +                       return;
> +               }
> +       }
> +       host->tune_cmd_counter = 0;
>         mmc_request_done(host->mmc, mrq);
>
>         pm_runtime_mark_last_busy(host->dev);
> @@ -725,11 +745,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
>         if (done)
>                 return true;
>
> -       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> -       writel(cmd->arg, host->base + SDC_ARG);
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>
>         if (cmd->flags & MMC_RSP_PRESENT) {
>                 if (cmd->flags & MMC_RSP_136) {
> @@ -819,10 +835,7 @@ static void msdc_start_command(struct msdc_host *host,
>         rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
>         mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
>
> -       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> +       sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>         writel(cmd->arg, host->base + SDC_ARG);
>         writel(rawcmd, host->base + SDC_CMD);
>  }
> @@ -942,6 +955,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
>
>                         if (events & MSDC_INT_DATTMO)
>                                 data->error = -ETIMEDOUT;
> +                       else if (events & MSDC_INT_DATCRCERR)
> +                               data->error = -EILSEQ;
>
>                         dev_err(host->dev, "%s: cmd=%d; blocks=%d",
>                                 __func__, mrq->cmd->opcode, data->blocks);
> @@ -1113,8 +1128,8 @@ static void msdc_init_hw(struct msdc_host *host)
>
>         writel(0, host->base + MSDC_PAD_TUNE);
>         writel(0, host->base + MSDC_IOCON);
> -       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
> -       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
> +       writel(0x403c0046, host->base + MSDC_PATCH_BIT);
>         sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
>         writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
>         /* Configure to enable SDIO mode.
> @@ -1176,6 +1191,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>         switch (ios->power_mode) {
>         case MMC_POWER_UP:
>                 if (!IS_ERR(mmc->supply.vmmc)) {
> +                       msdc_init_hw(host);
>                         ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
>                                         ios->vdd);
>                         if (ret) {
> @@ -1214,6 +1230,120 @@ end:
>         pm_runtime_put_autosuspend(host->dev);
>  }
>
> +static void msdc_reset_mrq(struct mmc_request *mrq)
> +{
> +       mrq->cmd->error = 0;
> +       if (mrq->sbc)
> +               mrq->sbc->error = 0;
> +       if (mrq->data)
> +               mrq->data->error = 0;
> +       if (mrq->stop)
> +               mrq->stop->error = 0;
> +}
> +
> +/* Send CMD12 when tuning, do not check CRC error and timeout */
> +static void msdc_send_stop(struct msdc_host *host)
> +{
> +       u32 opcode = MMC_STOP_TRANSMISSION;
> +       u32 arg = 0;
> +       u32 rawcmd = 0;
> +       u32 intsts = 0;
> +
> +       /* Reset host first */
> +       msdc_reset_hw(host);
> +
> +       rawcmd = (opcode & 0x3f) | (7 << 7);
> +       rawcmd |= (1 << 14); /* stop cmd */
> +
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> +       writel(arg, host->base + SDC_ARG);
> +       writel(rawcmd, host->base + SDC_CMD);
> +
> +       while (1) {
> +               intsts = readl(host->base + MSDC_INT);
> +               if (intsts) {
> +                       writel(intsts, host->base + MSDC_INT);
> +                       if (intsts & cmd_ints_mask) {
> +                               dev_dbg(host->dev, "result of cmd12: %x\n",
> +                                               intsts);
> +                               break;
> +                       }
> +               }
> +               udelay(1);
> +       }
> +}
> +
> +/* When tuning, CMD13 may also get crc error, so use MSDC_PS to get card status */
> +static void msdc_wait_card_not_busy(struct msdc_host *host)
> +{
> +       while (1) {
> +               if ((readl(host->base + MSDC_PS) & BIT(16)) == 0) { /* check dat0 status */
> +                       msleep_interruptible(10);
> +                       dev_dbg(host->dev, "MSDC_PS: %08x, SDC_STS: %08x\n",
> +                               readl(host->base + MSDC_PS), readl(host->base + SDC_STS));
> +               } else
> +                       break;
> +       }
> +}
> +
> +static void msdc_tune_request(struct msdc_host *host)
> +{
> +       u32 orig_rsmpl, orig_cksel;
> +       u32 cur_rsmpl, cur_cksel = 0;
> +       u32 ddr, clkmode;
> +       struct mmc_ios ios = host->mmc->ios;
> +
> +       sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
> +       sdr_get_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL,
> +                     &orig_cksel);
> +       cur_rsmpl = (orig_rsmpl + 1);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl % 2);
> +
> +       if (ios.timing != MMC_TIMING_MMC_HS400) {
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL,
> +                             cur_rsmpl % 2);
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL,
> +                             cur_rsmpl % 2);
> +       }
> +
> +       if (cur_rsmpl >= 2) {
> +               cur_cksel = orig_cksel + 1;
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, cur_cksel % 16);
> +       }
> +
> +       if (host->tune_cmd_counter++ >= 2 * 16) {
> +               dev_warn(host->dev, "Tune fail at %dhz\n", host->mclk);
> +               host->tune_cmd_counter = 0;
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, 0);
> +               sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
> +               ddr = (clkmode == 2) ? 1 : 0;
> +               msdc_set_mclk(host, ddr, host->mclk / 2);
> +       }
> +}
> +
> +static void msdc_repeat_request(struct work_struct *work)
> +{
> +       struct msdc_host *host = container_of(work, struct msdc_host, repeat_req);
> +       struct mmc_request *mrq;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +       mrq = host->repeat_mrq;
> +       host->repeat_mrq = NULL;
> +       spin_unlock_irqrestore(&host->lock, flags);
> +       msdc_send_stop(host);
> +       msdc_wait_card_not_busy(host);
> +       if (mrq->data && mrq->data->error == -ETIMEDOUT)
> +               mmc_hw_reset(host->mmc);
> +       else
> +               msdc_tune_request(host);
> +       msdc_reset_mrq(mrq);
> +       msdc_ops_request(host->mmc, mrq);
> +}
> +
>  static struct mmc_host_ops mt_msdc_ops = {
>         .post_req = msdc_post_req,
>         .pre_req = msdc_pre_req,
> @@ -1324,6 +1454,12 @@ static int msdc_drv_probe(struct platform_device *pdev)
>         }
>         msdc_init_gpd_bd(host, &host->dma);
>         INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
> +       host->repeat_workqueue = create_singlethread_workqueue("repeat_workqueue");
> +       if (!host->repeat_workqueue) {
> +               ret = -ENOMEM;
> +               goto release_mem;
> +       }
> +       INIT_WORK(&host->repeat_req, msdc_repeat_request);
>         spin_lock_init(&host->lock);
>
>         platform_set_drvdata(pdev, mmc);
> @@ -1348,6 +1484,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
>  end:
>         pm_runtime_disable(host->dev);
>  release:
> +       destroy_workqueue(host->repeat_workqueue);
>         platform_set_drvdata(pdev, NULL);
>         msdc_deinit_hw(host);
>         msdc_gate_clock(host);
> @@ -1383,6 +1520,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
>
>         pm_runtime_disable(host->dev);
>         pm_runtime_put_noidle(host->dev);
> +       destroy_workqueue(host->repeat_workqueue);
>         dma_free_coherent(&pdev->dev,
>                         sizeof(struct mt_gpdma_desc),
>                         host->dma.gpd, host->dma.gpd_addr);
> --
> 1.8.1.1.dirty
>

WARNING: multiple messages have this Message-ID (diff)
From: Ulf Hansson <ulf.hansson@linaro.org>
To: Chaotian Jing <chaotian.jing@mediatek.com>
Cc: "Rob Herring" <robh+dt@kernel.org>,
	"Matthias Brugger" <matthias.bgg@gmail.com>,
	"Chris Ball" <chris@printf.net>,
	"Mark Rutland" <mark.rutland@arm.com>,
	"James Liao" <jamesjj.liao@mediatek.com>,
	srv_heupstream <srv_heupstream@mediatek.com>,
	"Arnd Bergmann" <arnd@arndb.de>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"Hongzhou Yang" <hongzhou.yang@mediatek.com>,
	"Catalin Marinas" <catalin.marinas@arm.com>,
	linux-mmc <linux-mmc@vger.kernel.org>,
	"Will Deacon" <will.deacon@arm.com>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"linux-gpio@vger.kernel.org" <linux-gpio@vger.kernel.org>,
	"Sascha Hauer" <kernel@pengutronix.de>,
	"Joe.C" <yingjoe.chen@mediatek.com>,
	"Eddie Huang" <eddie.huang@mediatek.com>,
	"Bin Zhang (章斌)" <bin.zhang@mediatek.com>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	linux-mediatek@lists.infradead.org,
	"Russell King - ARM Linux" <linux@arm.linux.org.uk>,
	"Daniel Kurtz" <djkurtz@chromium.org>,
	"Yong Mao" <yong.mao@mediatek.com>,
	"Liuquan Ji" <liuquan.ji@mediatek.com>,
	"Wenbin Mei" <sin_wenbinmei@mediatek.com>
Subject: Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
Date: Mon, 17 Aug 2015 13:31:58 +0200	[thread overview]
Message-ID: <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ@mail.gmail.com> (raw)
In-Reply-To: <1439367845-5891-2-git-send-email-chaotian.jing@mediatek.com>

On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
> Schedule a workqueue to do tuning when CRC error
> Call mmc_hw_reset to re-init card when data timeout

Thanks to Adrian Hunter, the mmc core already supports re-tuning for
the above scenarios through the mmc_retune_*() APIs.

SDHCI driver has already adopted to use that feature, you should do
that for the mtk-sd driver as well.

Kind regards
Uffe

>
> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> ---
>  drivers/mmc/host/mtk-sd.c | 162 ++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 150 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 7153500..eb44fe1 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -27,6 +27,7 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/spinlock.h>
> +#include <linux/workqueue.h>
>
>  #include <linux/mmc/card.h>
>  #include <linux/mmc/core.h>
> @@ -290,6 +291,10 @@ struct msdc_host {
>         struct pinctrl_state *pins_default;
>         struct pinctrl_state *pins_uhs;
>         struct delayed_work req_timeout;
> +       struct workqueue_struct *repeat_workqueue;
> +       struct work_struct repeat_req;
> +       struct mmc_request *repeat_mrq;
> +       u32 tune_cmd_counter;
>         int irq;                /* host interrupt */
>
>         struct clk *src_clk;    /* msdc source clock */
> @@ -353,7 +358,10 @@ static void msdc_reset_hw(struct msdc_host *host)
>  static void msdc_cmd_next(struct msdc_host *host,
>                 struct mmc_request *mrq, struct mmc_command *cmd);
>
> -static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
> +static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
> +                       MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
> +                       MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
> +static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
>                         MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
>                         MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
>
> @@ -690,6 +698,18 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
>         msdc_track_cmd_data(host, mrq->cmd, mrq->data);
>         if (mrq->data)
>                 msdc_unprepare_data(host, mrq);
> +       if (host->error && host->mmc->card &&
> +           !mmc_card_sdio(host->mmc->card)) {
> +               if (mrq->cmd->error == (unsigned int)-EILSEQ ||
> +                   (mrq->stop && mrq->stop->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->sbc && mrq->sbc->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->data && mrq->data->error))  {
> +                       host->repeat_mrq = mrq;
> +                       queue_work(host->repeat_workqueue, &host->repeat_req);
> +                       return;
> +               }
> +       }
> +       host->tune_cmd_counter = 0;
>         mmc_request_done(host->mmc, mrq);
>
>         pm_runtime_mark_last_busy(host->dev);
> @@ -725,11 +745,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
>         if (done)
>                 return true;
>
> -       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> -       writel(cmd->arg, host->base + SDC_ARG);
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>
>         if (cmd->flags & MMC_RSP_PRESENT) {
>                 if (cmd->flags & MMC_RSP_136) {
> @@ -819,10 +835,7 @@ static void msdc_start_command(struct msdc_host *host,
>         rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
>         mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
>
> -       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> +       sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>         writel(cmd->arg, host->base + SDC_ARG);
>         writel(rawcmd, host->base + SDC_CMD);
>  }
> @@ -942,6 +955,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
>
>                         if (events & MSDC_INT_DATTMO)
>                                 data->error = -ETIMEDOUT;
> +                       else if (events & MSDC_INT_DATCRCERR)
> +                               data->error = -EILSEQ;
>
>                         dev_err(host->dev, "%s: cmd=%d; blocks=%d",
>                                 __func__, mrq->cmd->opcode, data->blocks);
> @@ -1113,8 +1128,8 @@ static void msdc_init_hw(struct msdc_host *host)
>
>         writel(0, host->base + MSDC_PAD_TUNE);
>         writel(0, host->base + MSDC_IOCON);
> -       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
> -       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
> +       writel(0x403c0046, host->base + MSDC_PATCH_BIT);
>         sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
>         writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
>         /* Configure to enable SDIO mode.
> @@ -1176,6 +1191,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>         switch (ios->power_mode) {
>         case MMC_POWER_UP:
>                 if (!IS_ERR(mmc->supply.vmmc)) {
> +                       msdc_init_hw(host);
>                         ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
>                                         ios->vdd);
>                         if (ret) {
> @@ -1214,6 +1230,120 @@ end:
>         pm_runtime_put_autosuspend(host->dev);
>  }
>
> +static void msdc_reset_mrq(struct mmc_request *mrq)
> +{
> +       mrq->cmd->error = 0;
> +       if (mrq->sbc)
> +               mrq->sbc->error = 0;
> +       if (mrq->data)
> +               mrq->data->error = 0;
> +       if (mrq->stop)
> +               mrq->stop->error = 0;
> +}
> +
> +/* Send CMD12 when tuning, do not check CRC error and timeout */
> +static void msdc_send_stop(struct msdc_host *host)
> +{
> +       u32 opcode = MMC_STOP_TRANSMISSION;
> +       u32 arg = 0;
> +       u32 rawcmd = 0;
> +       u32 intsts = 0;
> +
> +       /* Reset host first */
> +       msdc_reset_hw(host);
> +
> +       rawcmd = (opcode & 0x3f) | (7 << 7);
> +       rawcmd |= (1 << 14); /* stop cmd */
> +
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> +       writel(arg, host->base + SDC_ARG);
> +       writel(rawcmd, host->base + SDC_CMD);
> +
> +       while (1) {
> +               intsts = readl(host->base + MSDC_INT);
> +               if (intsts) {
> +                       writel(intsts, host->base + MSDC_INT);
> +                       if (intsts & cmd_ints_mask) {
> +                               dev_dbg(host->dev, "result of cmd12: %x\n",
> +                                               intsts);
> +                               break;
> +                       }
> +               }
> +               udelay(1);
> +       }
> +}
> +
> +/* When tuning, CMD13 may also get crc error, so use MSDC_PS to get card status */
> +static void msdc_wait_card_not_busy(struct msdc_host *host)
> +{
> +       while (1) {
> +               if ((readl(host->base + MSDC_PS) & BIT(16)) == 0) { /* check dat0 status */
> +                       msleep_interruptible(10);
> +                       dev_dbg(host->dev, "MSDC_PS: %08x, SDC_STS: %08x\n",
> +                               readl(host->base + MSDC_PS), readl(host->base + SDC_STS));
> +               } else
> +                       break;
> +       }
> +}
> +
> +static void msdc_tune_request(struct msdc_host *host)
> +{
> +       u32 orig_rsmpl, orig_cksel;
> +       u32 cur_rsmpl, cur_cksel = 0;
> +       u32 ddr, clkmode;
> +       struct mmc_ios ios = host->mmc->ios;
> +
> +       sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
> +       sdr_get_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL,
> +                     &orig_cksel);
> +       cur_rsmpl = (orig_rsmpl + 1);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl % 2);
> +
> +       if (ios.timing != MMC_TIMING_MMC_HS400) {
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL,
> +                             cur_rsmpl % 2);
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL,
> +                             cur_rsmpl % 2);
> +       }
> +
> +       if (cur_rsmpl >= 2) {
> +               cur_cksel = orig_cksel + 1;
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, cur_cksel % 16);
> +       }
> +
> +       if (host->tune_cmd_counter++ >= 2 * 16) {
> +               dev_warn(host->dev, "Tune fail at %dhz\n", host->mclk);
> +               host->tune_cmd_counter = 0;
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, 0);
> +               sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
> +               ddr = (clkmode == 2) ? 1 : 0;
> +               msdc_set_mclk(host, ddr, host->mclk / 2);
> +       }
> +}
> +
> +static void msdc_repeat_request(struct work_struct *work)
> +{
> +       struct msdc_host *host = container_of(work, struct msdc_host, repeat_req);
> +       struct mmc_request *mrq;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +       mrq = host->repeat_mrq;
> +       host->repeat_mrq = NULL;
> +       spin_unlock_irqrestore(&host->lock, flags);
> +       msdc_send_stop(host);
> +       msdc_wait_card_not_busy(host);
> +       if (mrq->data && mrq->data->error == -ETIMEDOUT)
> +               mmc_hw_reset(host->mmc);
> +       else
> +               msdc_tune_request(host);
> +       msdc_reset_mrq(mrq);
> +       msdc_ops_request(host->mmc, mrq);
> +}
> +
>  static struct mmc_host_ops mt_msdc_ops = {
>         .post_req = msdc_post_req,
>         .pre_req = msdc_pre_req,
> @@ -1324,6 +1454,12 @@ static int msdc_drv_probe(struct platform_device *pdev)
>         }
>         msdc_init_gpd_bd(host, &host->dma);
>         INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
> +       host->repeat_workqueue = create_singlethread_workqueue("repeat_workqueue");
> +       if (!host->repeat_workqueue) {
> +               ret = -ENOMEM;
> +               goto release_mem;
> +       }
> +       INIT_WORK(&host->repeat_req, msdc_repeat_request);
>         spin_lock_init(&host->lock);
>
>         platform_set_drvdata(pdev, mmc);
> @@ -1348,6 +1484,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
>  end:
>         pm_runtime_disable(host->dev);
>  release:
> +       destroy_workqueue(host->repeat_workqueue);
>         platform_set_drvdata(pdev, NULL);
>         msdc_deinit_hw(host);
>         msdc_gate_clock(host);
> @@ -1383,6 +1520,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
>
>         pm_runtime_disable(host->dev);
>         pm_runtime_put_noidle(host->dev);
> +       destroy_workqueue(host->repeat_workqueue);
>         dma_free_coherent(&pdev->dev,
>                         sizeof(struct mt_gpdma_desc),
>                         host->dma.gpd, host->dma.gpd_addr);
> --
> 1.8.1.1.dirty
>

WARNING: multiple messages have this Message-ID (diff)
From: ulf.hansson@linaro.org (Ulf Hansson)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
Date: Mon, 17 Aug 2015 13:31:58 +0200	[thread overview]
Message-ID: <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ@mail.gmail.com> (raw)
In-Reply-To: <1439367845-5891-2-git-send-email-chaotian.jing@mediatek.com>

On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
> Schedule a workqueue to do tuning when CRC error
> Call mmc_hw_reset to re-init card when data timeout

Thanks to Adrian Hunter, the mmc core already supports re-tuning for
the above scenarios through the mmc_retune_*() APIs.

SDHCI driver has already adopted to use that feature, you should do
that for the mtk-sd driver as well.

Kind regards
Uffe

>
> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> ---
>  drivers/mmc/host/mtk-sd.c | 162 ++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 150 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 7153500..eb44fe1 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -27,6 +27,7 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/spinlock.h>
> +#include <linux/workqueue.h>
>
>  #include <linux/mmc/card.h>
>  #include <linux/mmc/core.h>
> @@ -290,6 +291,10 @@ struct msdc_host {
>         struct pinctrl_state *pins_default;
>         struct pinctrl_state *pins_uhs;
>         struct delayed_work req_timeout;
> +       struct workqueue_struct *repeat_workqueue;
> +       struct work_struct repeat_req;
> +       struct mmc_request *repeat_mrq;
> +       u32 tune_cmd_counter;
>         int irq;                /* host interrupt */
>
>         struct clk *src_clk;    /* msdc source clock */
> @@ -353,7 +358,10 @@ static void msdc_reset_hw(struct msdc_host *host)
>  static void msdc_cmd_next(struct msdc_host *host,
>                 struct mmc_request *mrq, struct mmc_command *cmd);
>
> -static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
> +static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
> +                       MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
> +                       MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
> +static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
>                         MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
>                         MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
>
> @@ -690,6 +698,18 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
>         msdc_track_cmd_data(host, mrq->cmd, mrq->data);
>         if (mrq->data)
>                 msdc_unprepare_data(host, mrq);
> +       if (host->error && host->mmc->card &&
> +           !mmc_card_sdio(host->mmc->card)) {
> +               if (mrq->cmd->error == (unsigned int)-EILSEQ ||
> +                   (mrq->stop && mrq->stop->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->sbc && mrq->sbc->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->data && mrq->data->error))  {
> +                       host->repeat_mrq = mrq;
> +                       queue_work(host->repeat_workqueue, &host->repeat_req);
> +                       return;
> +               }
> +       }
> +       host->tune_cmd_counter = 0;
>         mmc_request_done(host->mmc, mrq);
>
>         pm_runtime_mark_last_busy(host->dev);
> @@ -725,11 +745,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
>         if (done)
>                 return true;
>
> -       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> -       writel(cmd->arg, host->base + SDC_ARG);
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>
>         if (cmd->flags & MMC_RSP_PRESENT) {
>                 if (cmd->flags & MMC_RSP_136) {
> @@ -819,10 +835,7 @@ static void msdc_start_command(struct msdc_host *host,
>         rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
>         mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
>
> -       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> +       sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>         writel(cmd->arg, host->base + SDC_ARG);
>         writel(rawcmd, host->base + SDC_CMD);
>  }
> @@ -942,6 +955,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
>
>                         if (events & MSDC_INT_DATTMO)
>                                 data->error = -ETIMEDOUT;
> +                       else if (events & MSDC_INT_DATCRCERR)
> +                               data->error = -EILSEQ;
>
>                         dev_err(host->dev, "%s: cmd=%d; blocks=%d",
>                                 __func__, mrq->cmd->opcode, data->blocks);
> @@ -1113,8 +1128,8 @@ static void msdc_init_hw(struct msdc_host *host)
>
>         writel(0, host->base + MSDC_PAD_TUNE);
>         writel(0, host->base + MSDC_IOCON);
> -       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
> -       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
> +       writel(0x403c0046, host->base + MSDC_PATCH_BIT);
>         sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
>         writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
>         /* Configure to enable SDIO mode.
> @@ -1176,6 +1191,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>         switch (ios->power_mode) {
>         case MMC_POWER_UP:
>                 if (!IS_ERR(mmc->supply.vmmc)) {
> +                       msdc_init_hw(host);
>                         ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
>                                         ios->vdd);
>                         if (ret) {
> @@ -1214,6 +1230,120 @@ end:
>         pm_runtime_put_autosuspend(host->dev);
>  }
>
> +static void msdc_reset_mrq(struct mmc_request *mrq)
> +{
> +       mrq->cmd->error = 0;
> +       if (mrq->sbc)
> +               mrq->sbc->error = 0;
> +       if (mrq->data)
> +               mrq->data->error = 0;
> +       if (mrq->stop)
> +               mrq->stop->error = 0;
> +}
> +
> +/* Send CMD12 when tuning, do not check CRC error and timeout */
> +static void msdc_send_stop(struct msdc_host *host)
> +{
> +       u32 opcode = MMC_STOP_TRANSMISSION;
> +       u32 arg = 0;
> +       u32 rawcmd = 0;
> +       u32 intsts = 0;
> +
> +       /* Reset host first */
> +       msdc_reset_hw(host);
> +
> +       rawcmd = (opcode & 0x3f) | (7 << 7);
> +       rawcmd |= (1 << 14); /* stop cmd */
> +
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> +       writel(arg, host->base + SDC_ARG);
> +       writel(rawcmd, host->base + SDC_CMD);
> +
> +       while (1) {
> +               intsts = readl(host->base + MSDC_INT);
> +               if (intsts) {
> +                       writel(intsts, host->base + MSDC_INT);
> +                       if (intsts & cmd_ints_mask) {
> +                               dev_dbg(host->dev, "result of cmd12: %x\n",
> +                                               intsts);
> +                               break;
> +                       }
> +               }
> +               udelay(1);
> +       }
> +}
> +
> +/* When tuning, CMD13 may also get crc error, so use MSDC_PS to get card status */
> +static void msdc_wait_card_not_busy(struct msdc_host *host)
> +{
> +       while (1) {
> +               if ((readl(host->base + MSDC_PS) & BIT(16)) == 0) { /* check dat0 status */
> +                       msleep_interruptible(10);
> +                       dev_dbg(host->dev, "MSDC_PS: %08x, SDC_STS: %08x\n",
> +                               readl(host->base + MSDC_PS), readl(host->base + SDC_STS));
> +               } else
> +                       break;
> +       }
> +}
> +
> +static void msdc_tune_request(struct msdc_host *host)
> +{
> +       u32 orig_rsmpl, orig_cksel;
> +       u32 cur_rsmpl, cur_cksel = 0;
> +       u32 ddr, clkmode;
> +       struct mmc_ios ios = host->mmc->ios;
> +
> +       sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
> +       sdr_get_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL,
> +                     &orig_cksel);
> +       cur_rsmpl = (orig_rsmpl + 1);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl % 2);
> +
> +       if (ios.timing != MMC_TIMING_MMC_HS400) {
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL,
> +                             cur_rsmpl % 2);
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL,
> +                             cur_rsmpl % 2);
> +       }
> +
> +       if (cur_rsmpl >= 2) {
> +               cur_cksel = orig_cksel + 1;
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, cur_cksel % 16);
> +       }
> +
> +       if (host->tune_cmd_counter++ >= 2 * 16) {
> +               dev_warn(host->dev, "Tune fail at %dhz\n", host->mclk);
> +               host->tune_cmd_counter = 0;
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, 0);
> +               sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
> +               ddr = (clkmode == 2) ? 1 : 0;
> +               msdc_set_mclk(host, ddr, host->mclk / 2);
> +       }
> +}
> +
> +static void msdc_repeat_request(struct work_struct *work)
> +{
> +       struct msdc_host *host = container_of(work, struct msdc_host, repeat_req);
> +       struct mmc_request *mrq;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +       mrq = host->repeat_mrq;
> +       host->repeat_mrq = NULL;
> +       spin_unlock_irqrestore(&host->lock, flags);
> +       msdc_send_stop(host);
> +       msdc_wait_card_not_busy(host);
> +       if (mrq->data && mrq->data->error == -ETIMEDOUT)
> +               mmc_hw_reset(host->mmc);
> +       else
> +               msdc_tune_request(host);
> +       msdc_reset_mrq(mrq);
> +       msdc_ops_request(host->mmc, mrq);
> +}
> +
>  static struct mmc_host_ops mt_msdc_ops = {
>         .post_req = msdc_post_req,
>         .pre_req = msdc_pre_req,
> @@ -1324,6 +1454,12 @@ static int msdc_drv_probe(struct platform_device *pdev)
>         }
>         msdc_init_gpd_bd(host, &host->dma);
>         INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
> +       host->repeat_workqueue = create_singlethread_workqueue("repeat_workqueue");
> +       if (!host->repeat_workqueue) {
> +               ret = -ENOMEM;
> +               goto release_mem;
> +       }
> +       INIT_WORK(&host->repeat_req, msdc_repeat_request);
>         spin_lock_init(&host->lock);
>
>         platform_set_drvdata(pdev, mmc);
> @@ -1348,6 +1484,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
>  end:
>         pm_runtime_disable(host->dev);
>  release:
> +       destroy_workqueue(host->repeat_workqueue);
>         platform_set_drvdata(pdev, NULL);
>         msdc_deinit_hw(host);
>         msdc_gate_clock(host);
> @@ -1383,6 +1520,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
>
>         pm_runtime_disable(host->dev);
>         pm_runtime_put_noidle(host->dev);
> +       destroy_workqueue(host->repeat_workqueue);
>         dma_free_coherent(&pdev->dev,
>                         sizeof(struct mt_gpdma_desc),
>                         host->dma.gpd, host->dma.gpd_addr);
> --
> 1.8.1.1.dirty
>

  reply	other threads:[~2015-08-17 11:31 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-12  8:24 [PATCH 0/4] msdc: Add tuning support of Mediatek MMC host Chaotian Jing
2015-08-12  8:24 ` Chaotian Jing
     [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2015-08-12  8:24   ` [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD Chaotian Jing
2015-08-12  8:24     ` Chaotian Jing
2015-08-17 11:31     ` Ulf Hansson [this message]
2015-08-17 11:31       ` Ulf Hansson
2015-08-17 11:31       ` Ulf Hansson
     [not found]       ` <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-08-17 12:01         ` Chaotian Jing
2015-08-17 12:01           ` Chaotian Jing
2015-08-25 12:07           ` Ulf Hansson
2015-08-25 12:07             ` Ulf Hansson
2015-08-25 12:07             ` Ulf Hansson
     [not found]             ` <CAPDyKFpGk9gcLLjBnN=+BD5WP-7dZ0+XHC1bcEEGGLvLbTyKHw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-08-26  8:40               ` Chaotian Jing
2015-08-26  8:40                 ` Chaotian Jing
2015-08-26 15:06                 ` Ulf Hansson
2015-08-26 15:06                   ` Ulf Hansson
2015-08-26 15:06                   ` Ulf Hansson
2015-08-12  8:24   ` [PATCH 2/4] mmc: mediatek: Add HS400 support Chaotian Jing
2015-08-12  8:24     ` Chaotian Jing
2015-08-12  8:24   ` [PATCH 3/4] arm64: dts: mediatek: Support SD/EMMC SDR104/HS200/HS400 Chaotian Jing
2015-08-12  8:24     ` Chaotian Jing
2015-08-12  8:24   ` [PATCH 4/4] mmc: dt-bindings: Add 400Mhz clock source Chaotian Jing
2015-08-12  8:24     ` Chaotian Jing

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ@mail.gmail.com \
    --to=ulf.hansson@linaro.org \
    --cc=arnd@arndb.de \
    --cc=bin.zhang@mediatek.com \
    --cc=catalin.marinas@arm.com \
    --cc=chaotian.jing@mediatek.com \
    --cc=chris@printf.net \
    --cc=devicetree@vger.kernel.org \
    --cc=eddie.huang@mediatek.com \
    --cc=hongzhou.yang@mediatek.com \
    --cc=jamesjj.liao@mediatek.com \
    --cc=kernel@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=matthias.bgg@gmail.com \
    --cc=robh+dt@kernel.org \
    --cc=srv_heupstream@mediatek.com \
    --cc=will.deacon@arm.com \
    --cc=yingjoe.chen@mediatek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.