All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yong Mao <yong.mao@mediatek.com>
To: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Will Deacon <will.deacon@arm.com>,
	Chaotian Jing <chaotian.jing@mediatek.com>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	Eddie Huang <eddie.huang@mediatek.com>,
	Chunfeng Yun <chunfeng.yun@mediatek.com>,
	Nicolas Boichat <drinkcat@chromium.org>,
	Douglas Anderson <dianders@chromium.org>,
	Javier Martinez Canillas <javier@osg.samsung.com>,
	"linux-mmc@vger.kernel.org" <linux-mmc@vger.kernel.org>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org" 
	<linux-arm-kernel@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	srv_heupstream <srv_heupstream@mediatek.com>
Subject: Re: [PATCH 1/2] mmc: mediatek: Use data tune for CMD line tune
Date: Fri, 13 Jan 2017 18:17:42 +0800	[thread overview]
Message-ID: <1484302662.10824.18.camel@mhfsdcap03> (raw)
In-Reply-To: <CAPDyKFq0_WpCAAmUDfaKApq7=tdpjNqL896FC=QPfGgE9JaEHA@mail.gmail.com>

On Thu, 2017-01-12 at 11:39 +0100, Ulf Hansson wrote:
> On 12 January 2017 at 11:04, Yong Mao <yong.mao@mediatek.com> wrote:
> > From: yong mao <yong.mao@mediatek.com>
> >
> > CMD response CRC error may cause cannot boot up
> > Change to use data tune for CMD line
> > Separate cmd internal delay for HS200/HS400 mode
> 
> Please try to work a little bit on improving the change log. Moreover
> as this is a fix for a regression (it seems like so?), please try to
> make that clear.
This change can fix CMD respose CRC issue in our platform.
I will try to make it clear in next version.

> 
> >
> > Signed-off-by: Yong Mao <yong.mao@mediatek.com>
> > Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> > ---
> >  arch/arm64/boot/dts/mediatek/mt8173-evb.dts |    3 +
> 
> Changes to the DTS files should be a separate change. Please split it
> into its own patch.
> 
> >  drivers/mmc/host/mtk-sd.c                   |  169 +++++++++++++++++++++++----
> >  2 files changed, 149 insertions(+), 23 deletions(-)
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > index 0ecaad4..29c3100 100644
> > --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > @@ -134,6 +134,9 @@
> >         bus-width = <8>;
> >         max-frequency = <50000000>;
> >         cap-mmc-highspeed;
> > +       hs200-cmd-int-delay = <26>;
> > +       hs400-cmd-int-delay = <14>;
> > +       cmd-resp-sel = <0>; /* 0: rising, 1: falling */
> >         vmmc-supply = <&mt6397_vemc_3v3_reg>;
> >         vqmmc-supply = <&mt6397_vio18_reg>;
> >         non-removable;
> > diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> > index 80ba034..93eb395 100644
> > --- a/drivers/mmc/host/mtk-sd.c
> > +++ b/drivers/mmc/host/mtk-sd.c
> > @@ -75,6 +75,7 @@
> >  #define MSDC_PATCH_BIT1  0xb4
> >  #define MSDC_PAD_TUNE    0xec
> >  #define PAD_DS_TUNE      0x188
> > +#define PAD_CMD_TUNE     0x18c
> >  #define EMMC50_CFG0      0x208
> >
> >  /*--------------------------------------------------------------------------*/
> > @@ -210,12 +211,17 @@
> >  #define MSDC_PATCH_BIT_SPCPUSH    (0x1 << 29)  /* RW */
> >  #define MSDC_PATCH_BIT_DECRCTMO   (0x1 << 30)  /* RW */
> >
> > -#define MSDC_PAD_TUNE_DATRRDLY   (0x1f <<  8)  /* RW */
> > -#define MSDC_PAD_TUNE_CMDRDLY    (0x1f << 16)  /* RW */
> > +#define MSDC_PAD_TUNE_DATWRDLY    (0x1f <<  0)  /* RW */
> > +#define MSDC_PAD_TUNE_DATRRDLY    (0x1f <<  8) /* RW */
> > +#define MSDC_PAD_TUNE_CMDRDLY     (0x1f << 16)  /* RW */
> > +#define MSDC_PAD_TUNE_CMDRRDLY    (0x1f << 22)  /* RW */
> 
> Is there a white space change somewhere here? I don't see any changes
> to MSDC_PAD_TUNE_DATRRDLY and MSDC_PAD_TUNE_CMDRDLY.
> 
Sorry. I will fix in next version.

> > +#define MSDC_PAD_TUNE_CLKTDLY     (0x1f << 27)  /* RW */
> >
> > -#define PAD_DS_TUNE_DLY1         (0x1f << 2)   /* RW */
> > -#define PAD_DS_TUNE_DLY2         (0x1f << 7)   /* RW */
> > -#define PAD_DS_TUNE_DLY3         (0x1f << 12)  /* RW */
> > +#define PAD_DS_TUNE_DLY1          (0x1f << 2)   /* RW */
> > +#define PAD_DS_TUNE_DLY2          (0x1f << 7)   /* RW */
> > +#define PAD_DS_TUNE_DLY3          (0x1f << 12)  /* RW */
> > +
> 
> Ditto.
Ditto.

> 
> > +#define PAD_CMD_TUNE_RX_DLY3      (0x1f << 1)   /* RW */
> >
> >  #define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0)   /* RW */
> >  #define EMMC50_CFG_CRCSTS_EDGE    (0x1 << 3)   /* RW */
> > @@ -236,7 +242,9 @@
> >  #define CMD_TIMEOUT         (HZ/10 * 5)        /* 100ms x5 */
> >  #define DAT_TIMEOUT         (HZ    * 5)        /* 1000ms x5 */
> >
> > -#define PAD_DELAY_MAX  32 /* PAD delay cells */
> > +#define PAD_DELAY_MAX            32 /* PAD delay cells */
> 
> Ditto.
> 
Ditto.

> > +#define ENOUGH_MARGIN_MIN        12 /* Enough Margin */
> > +#define PREFER_START_POS_MAX     4  /* Prefer start position */
> >  /*--------------------------------------------------------------------------*/
> >  /* Descriptor Structure                                                     */
> >  /*--------------------------------------------------------------------------*/
> > @@ -284,12 +292,14 @@ struct msdc_save_para {
> >         u32 patch_bit0;
> >         u32 patch_bit1;
> >         u32 pad_ds_tune;
> > +       u32 pad_cmd_tune;
> >         u32 emmc50_cfg0;
> >  };
> >
> >  struct msdc_tune_para {
> >         u32 iocon;
> >         u32 pad_tune;
> > +       u32 pad_cmd_tune;
> >  };
> >
> >  struct msdc_delay_phase {
> > @@ -331,6 +341,9 @@ struct msdc_host {
> >         unsigned char timing;
> >         bool vqmmc_enabled;
> >         u32 hs400_ds_delay;
> > +       u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
> > +       u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
> > +       u32 hs200_cmd_resp_sel; /* cmd response sample selection */
> >         bool hs400_mode;        /* current eMMC will run at hs400 mode */
> >         struct msdc_save_para save_para; /* used when gate HCLK */
> >         struct msdc_tune_para def_tune_para; /* default tune setting */
> > @@ -596,12 +609,21 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
> >          */
> >         if (host->sclk <= 52000000) {
> >                 writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
> > -               writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> > +               writel(host->def_tune_para.pad_tune,
> > +                      host->base + MSDC_PAD_TUNE);
> 
> Please don't change code just because you feel like doing it. This is
> a completely unessarry change and it makes it harder for me to review.
> 
> Can you go thorugh the complete patch and make sure to undo all
> similar changes, there are more of them.
> 
Sorry. I will fix in next version.

> >         } else {
> > -               writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
> > -               writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> > +               writel(host->saved_tune_para.iocon,
> > +                      host->base + MSDC_IOCON);
> > +               writel(host->saved_tune_para.pad_tune,
> > +                      host->base + MSDC_PAD_TUNE);
> > +               writel(host->saved_tune_para.pad_cmd_tune,
> > +                      host->base + PAD_CMD_TUNE);
> >         }
> >
> > +       if (timing == MMC_TIMING_MMC_HS400)
> > +               sdr_set_field(host->base + PAD_CMD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs400_cmd_int_delay);
> >         dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
> >  }
> >
> > @@ -1302,7 +1324,8 @@ static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
> >                         len_final = len;
> >                 }
> >                 start += len ? len : 1;
> > -               if (len >= 8 && start_final < 4)
> > +               if (len >= ENOUGH_MARGIN_MIN &&
> > +                   start_final < PREFER_START_POS_MAX)
> >                         break;
> >         }
> >
> > @@ -1325,48 +1348,128 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
> >         struct msdc_host *host = mmc_priv(mmc);
> >         u32 rise_delay = 0, fall_delay = 0;
> >         struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
> > +       struct msdc_delay_phase internal_delay_phase;
> >         u8 final_delay, final_maxlen;
> > +       u32 internal_delay = 0;
> >         int cmd_err;
> > -       int i;
> > +       int i, j;
> >
> > +       if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> > +           mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs200_cmd_int_delay);
> >         sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> >         for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> >                 sdr_set_field(host->base + MSDC_PAD_TUNE,
> >                               MSDC_PAD_TUNE_CMDRDLY, i);
> > -               mmc_send_tuning(mmc, opcode, &cmd_err);
> > -               if (!cmd_err)
> > -                       rise_delay |= (1 << i);
> > +               for (j = 0; j < 3; j++) {
> 
> Any reason to why looping three times makes sense? Maybe add a comment?
> 
Using the same parameters, it may sometimes pass the test,
but sometimes it may fail. To make sure the parameters is 
more stable, we test 3 times for on set of parameters.

Anyway, I will add comment here in next version.

> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               rise_delay |= (1 << i);
> > +                       } else {
> > +                               rise_delay &= ~(1 << i);
> > +                               break;
> > +                       }
> > +               }
> >         }
> >         final_rise_delay = get_best_delay(host, rise_delay);
> >         /* if rising edge has enough margin, then do not scan falling edge */
> > -       if (final_rise_delay.maxlen >= 10 ||
> > -           (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> > +       if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> > +           final_rise_delay.start < PREFER_START_POS_MAX)
> 
> This looks like clean-ups, as you are converting from magic numbers to
> defines. Please make this kind of changes separately.
I will drop this change in next version.

> 
> >                 goto skip_fall;
> >
> >         sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> >         for (i = 0; i < PAD_DELAY_MAX; i++) {
> >                 sdr_set_field(host->base + MSDC_PAD_TUNE,
> >                               MSDC_PAD_TUNE_CMDRDLY, i);
> > -               mmc_send_tuning(mmc, opcode, &cmd_err);
> > -               if (!cmd_err)
> > -                       fall_delay |= (1 << i);
> > +               for (j = 0; j < 3; j++) {
> 
> 3?
> 
> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               fall_delay |= (1 << i);
> > +                       } else {
> > +                               fall_delay &= ~(1 << i);
> > +                               break;
> > +                       };
> > +               }
> >         }
> >         final_fall_delay = get_best_delay(host, fall_delay);
> >
> >  skip_fall:
> >         final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
> > +       if (final_fall_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> > +           final_fall_delay.start < PREFER_START_POS_MAX)
> > +               final_maxlen = final_fall_delay.maxlen;
> >         if (final_maxlen == final_rise_delay.maxlen) {
> >                 sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> > -               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRDLY,
> >                               final_rise_delay.final_phase);
> >                 final_delay = final_rise_delay.final_phase;
> >         } else {
> >                 sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> > -               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRDLY,
> >                               final_fall_delay.final_phase);
> >                 final_delay = final_fall_delay.final_phase;
> >         }
> > +       if (host->hs200_cmd_int_delay)
> > +               goto skip_internal;
> >
> > +       for (i = 0; i < PAD_DELAY_MAX; i++) {
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY, i);
> > +               mmc_send_tuning(mmc, opcode, &cmd_err);
> > +               if (!cmd_err)
> > +                       internal_delay |= (1 << i);
> > +       }
> > +       dev_info(host->dev, "Final internal delay: 0x%x\n", internal_delay);
> 
> I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
> anything at all.
> 
Yes. We should use dev_dbg() here. Will be fixed in next version.

> > +       internal_delay_phase = get_best_delay(host, internal_delay);
> > +       sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
> > +                     internal_delay_phase.final_phase);
> > +skip_internal:
> > +       dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
> 
> I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
> anything at all.
> 
Yes. We should use dev_dbg() here. Will be fixed in next version.

> > +       return final_delay == 0xff ? -EIO : 0;
> > +}
> > +
> > +static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
> > +{
> > +       struct msdc_host *host = mmc_priv(mmc);
> > +       u32 cmd_delay = 0;
> > +       struct msdc_delay_phase final_cmd_delay = { 0,};
> > +       u8 final_delay;
> > +       int cmd_err;
> > +       int i, j;
> > +
> > +       /* select EMMC50 PAD CMD tune */
> > +       sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
> > +
> > +       if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> > +           mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs200_cmd_int_delay);
> > +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL,
> > +                     host->hs200_cmd_resp_sel);
> > +       for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> > +               sdr_set_field(host->base + PAD_CMD_TUNE,
> > +                             PAD_CMD_TUNE_RX_DLY3, i);
> > +               for (j = 0; j < 3; j++) {
> 
> 3?
> 
> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               cmd_delay |= (1 << i);
> > +                       } else {
> > +                               cmd_delay &= ~(1 << i);
> > +                               break;
> > +                       }
> > +               }
> > +       }
> > +       final_cmd_delay = get_best_delay(host, cmd_delay);
> > +       sdr_set_field(host->base + PAD_CMD_TUNE, PAD_CMD_TUNE_RX_DLY3,
> > +                     final_cmd_delay.final_phase);
> > +       final_delay = final_cmd_delay.final_phase;
> > +
> > +       dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
> 
> dev_info() -> dev_dbg() or remove it.
We will use dev_dbg() instead of dev_info(). Will be fixed in next
version.

> 
> >         return final_delay == 0xff ? -EIO : 0;
> >  }
> >
> > @@ -1389,7 +1492,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> >         }
> >         final_rise_delay = get_best_delay(host, rise_delay);
> >         /* if rising edge has enough margin, then do not scan falling edge */
> > -       if (final_rise_delay.maxlen >= 10 ||
> > +       if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN ||
> 
> Clean up. Move to separate change.
> 
Will drop it in current change.

> >             (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> >                 goto skip_fall;
> >
> > @@ -1422,6 +1525,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> >                 final_delay = final_fall_delay.final_phase;
> >         }
> >
> > +       dev_info(host->dev, "Final data pad delay: %x\n", final_delay);
> 
> dev_info() -> dev_dbg() or remove it.
> 
We will use dev_dbg() instead of dev_info(). Will be fixed in next
version.

> >         return final_delay == 0xff ? -EIO : 0;
> >  }
> >
> > @@ -1430,10 +1534,13 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> >         struct msdc_host *host = mmc_priv(mmc);
> >         int ret;
> >
> > +       if (host->hs400_mode)
> > +               ret = hs400_tune_response(mmc, opcode);
> > +       else
> >         ret = msdc_tune_response(mmc, opcode);
> 
> Because of the new else clause, seems like above needs an intendation.
> 
Sorry. Will be fixed in next version.

> >         if (ret == -EIO) {
> >                 dev_err(host->dev, "Tune response fail!\n");
> > -               return ret;
> > +               goto out;
> 
> Not needed, remove the label and this change.
> 
Will drop it in this change. 

> >         }
> >         if (host->hs400_mode == false) {
> >                 ret = msdc_tune_data(mmc, opcode);
> > @@ -1443,6 +1550,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> >
> >         host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
> >         host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
> > +       host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> > +out:
> >         return ret;
> >  }
> >
> > @@ -1553,6 +1662,18 @@ static int msdc_drv_probe(struct platform_device *pdev)
> >                 dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
> >                         host->hs400_ds_delay);
> >
> > +       if (!of_property_read_u32(pdev->dev.of_node, "hs200-cmd-int-delay",
> > +                                 &host->hs200_cmd_int_delay))
> > +               dev_dbg(&pdev->dev, "host->hs200-cmd-int-delay: %x\n",
> > +                       host->hs200_cmd_int_delay);
> > +       if (!of_property_read_u32(pdev->dev.of_node, "hs400-cmd-int-delay",
> > +                                 &host->hs400_cmd_int_delay))
> > +               dev_dbg(&pdev->dev, "host->hs400-cmd-int-delay: %x\n",
> > +                       host->hs400_cmd_int_delay);
> > +       if (!of_property_read_u32(pdev->dev.of_node, "cmd-resp-sel",
> > +                                 &host->hs200_cmd_resp_sel))
> > +               dev_dbg(&pdev->dev, "host->hs200_cmd-resp-sel: %x\n",
> > +                       host->hs200_cmd_resp_sel);
> 
> I suggest you take the oppotunity to move the MTK DTS parsing into its
> own function, include the existing parsing of the "hs400-ds-delay".
> This improve the readablitity of the code.
> 
Thanks. We will move all of MTK DTS parsing into msdc_of_property_parse
function.

> >         host->dev = &pdev->dev;
> >         host->mmc = mmc;
> >         host->src_clk_freq = clk_get_rate(host->src_clk);
> > @@ -1663,6 +1784,7 @@ static void msdc_save_reg(struct msdc_host *host)
> >         host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
> >         host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
> >         host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
> > +       host->save_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> >         host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
> >  }
> >
> > @@ -1675,6 +1797,7 @@ static void msdc_restore_reg(struct msdc_host *host)
> >         writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
> >         writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
> >         writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
> > +       writel(host->save_para.pad_cmd_tune, host->base + PAD_CMD_TUNE);
> >         writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
> >  }
> >
> > --
> > 1.7.9.5
> >
> 
> Kind regards
> Uffe
Best Regards,
Yong Mao.

WARNING: multiple messages have this Message-ID (diff)
From: Yong Mao <yong.mao@mediatek.com>
To: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Will Deacon <will.deacon@arm.com>,
	Chaotian Jing <chaotian.jing@mediatek.com>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Eddie Huang <eddie.huang@mediatek.com>,
	Chunfeng Yun <chunfeng.yun@mediatek.com>,
	Nicolas Boichat <drinkcat@chromium.org>,
	Douglas Anderson <dianders@chromium.org>,
	Javier Martinez Canillas <javier@osg.samsung.com>,
	"linux-mmc@vger.kernel.org" <linux-mmc@vger.kernel.org>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	linux-mediatek@lists
Subject: Re: [PATCH 1/2] mmc: mediatek: Use data tune for CMD line tune
Date: Fri, 13 Jan 2017 18:17:42 +0800	[thread overview]
Message-ID: <1484302662.10824.18.camel@mhfsdcap03> (raw)
In-Reply-To: <CAPDyKFq0_WpCAAmUDfaKApq7=tdpjNqL896FC=QPfGgE9JaEHA@mail.gmail.com>

On Thu, 2017-01-12 at 11:39 +0100, Ulf Hansson wrote:
> On 12 January 2017 at 11:04, Yong Mao <yong.mao@mediatek.com> wrote:
> > From: yong mao <yong.mao@mediatek.com>
> >
> > CMD response CRC error may cause cannot boot up
> > Change to use data tune for CMD line
> > Separate cmd internal delay for HS200/HS400 mode
> 
> Please try to work a little bit on improving the change log. Moreover
> as this is a fix for a regression (it seems like so?), please try to
> make that clear.
This change can fix CMD respose CRC issue in our platform.
I will try to make it clear in next version.

> 
> >
> > Signed-off-by: Yong Mao <yong.mao@mediatek.com>
> > Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> > ---
> >  arch/arm64/boot/dts/mediatek/mt8173-evb.dts |    3 +
> 
> Changes to the DTS files should be a separate change. Please split it
> into its own patch.
> 
> >  drivers/mmc/host/mtk-sd.c                   |  169 +++++++++++++++++++++++----
> >  2 files changed, 149 insertions(+), 23 deletions(-)
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > index 0ecaad4..29c3100 100644
> > --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > @@ -134,6 +134,9 @@
> >         bus-width = <8>;
> >         max-frequency = <50000000>;
> >         cap-mmc-highspeed;
> > +       hs200-cmd-int-delay = <26>;
> > +       hs400-cmd-int-delay = <14>;
> > +       cmd-resp-sel = <0>; /* 0: rising, 1: falling */
> >         vmmc-supply = <&mt6397_vemc_3v3_reg>;
> >         vqmmc-supply = <&mt6397_vio18_reg>;
> >         non-removable;
> > diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> > index 80ba034..93eb395 100644
> > --- a/drivers/mmc/host/mtk-sd.c
> > +++ b/drivers/mmc/host/mtk-sd.c
> > @@ -75,6 +75,7 @@
> >  #define MSDC_PATCH_BIT1  0xb4
> >  #define MSDC_PAD_TUNE    0xec
> >  #define PAD_DS_TUNE      0x188
> > +#define PAD_CMD_TUNE     0x18c
> >  #define EMMC50_CFG0      0x208
> >
> >  /*--------------------------------------------------------------------------*/
> > @@ -210,12 +211,17 @@
> >  #define MSDC_PATCH_BIT_SPCPUSH    (0x1 << 29)  /* RW */
> >  #define MSDC_PATCH_BIT_DECRCTMO   (0x1 << 30)  /* RW */
> >
> > -#define MSDC_PAD_TUNE_DATRRDLY   (0x1f <<  8)  /* RW */
> > -#define MSDC_PAD_TUNE_CMDRDLY    (0x1f << 16)  /* RW */
> > +#define MSDC_PAD_TUNE_DATWRDLY    (0x1f <<  0)  /* RW */
> > +#define MSDC_PAD_TUNE_DATRRDLY    (0x1f <<  8) /* RW */
> > +#define MSDC_PAD_TUNE_CMDRDLY     (0x1f << 16)  /* RW */
> > +#define MSDC_PAD_TUNE_CMDRRDLY    (0x1f << 22)  /* RW */
> 
> Is there a white space change somewhere here? I don't see any changes
> to MSDC_PAD_TUNE_DATRRDLY and MSDC_PAD_TUNE_CMDRDLY.
> 
Sorry. I will fix in next version.

> > +#define MSDC_PAD_TUNE_CLKTDLY     (0x1f << 27)  /* RW */
> >
> > -#define PAD_DS_TUNE_DLY1         (0x1f << 2)   /* RW */
> > -#define PAD_DS_TUNE_DLY2         (0x1f << 7)   /* RW */
> > -#define PAD_DS_TUNE_DLY3         (0x1f << 12)  /* RW */
> > +#define PAD_DS_TUNE_DLY1          (0x1f << 2)   /* RW */
> > +#define PAD_DS_TUNE_DLY2          (0x1f << 7)   /* RW */
> > +#define PAD_DS_TUNE_DLY3          (0x1f << 12)  /* RW */
> > +
> 
> Ditto.
Ditto.

> 
> > +#define PAD_CMD_TUNE_RX_DLY3      (0x1f << 1)   /* RW */
> >
> >  #define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0)   /* RW */
> >  #define EMMC50_CFG_CRCSTS_EDGE    (0x1 << 3)   /* RW */
> > @@ -236,7 +242,9 @@
> >  #define CMD_TIMEOUT         (HZ/10 * 5)        /* 100ms x5 */
> >  #define DAT_TIMEOUT         (HZ    * 5)        /* 1000ms x5 */
> >
> > -#define PAD_DELAY_MAX  32 /* PAD delay cells */
> > +#define PAD_DELAY_MAX            32 /* PAD delay cells */
> 
> Ditto.
> 
Ditto.

> > +#define ENOUGH_MARGIN_MIN        12 /* Enough Margin */
> > +#define PREFER_START_POS_MAX     4  /* Prefer start position */
> >  /*--------------------------------------------------------------------------*/
> >  /* Descriptor Structure                                                     */
> >  /*--------------------------------------------------------------------------*/
> > @@ -284,12 +292,14 @@ struct msdc_save_para {
> >         u32 patch_bit0;
> >         u32 patch_bit1;
> >         u32 pad_ds_tune;
> > +       u32 pad_cmd_tune;
> >         u32 emmc50_cfg0;
> >  };
> >
> >  struct msdc_tune_para {
> >         u32 iocon;
> >         u32 pad_tune;
> > +       u32 pad_cmd_tune;
> >  };
> >
> >  struct msdc_delay_phase {
> > @@ -331,6 +341,9 @@ struct msdc_host {
> >         unsigned char timing;
> >         bool vqmmc_enabled;
> >         u32 hs400_ds_delay;
> > +       u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
> > +       u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
> > +       u32 hs200_cmd_resp_sel; /* cmd response sample selection */
> >         bool hs400_mode;        /* current eMMC will run at hs400 mode */
> >         struct msdc_save_para save_para; /* used when gate HCLK */
> >         struct msdc_tune_para def_tune_para; /* default tune setting */
> > @@ -596,12 +609,21 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
> >          */
> >         if (host->sclk <= 52000000) {
> >                 writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
> > -               writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> > +               writel(host->def_tune_para.pad_tune,
> > +                      host->base + MSDC_PAD_TUNE);
> 
> Please don't change code just because you feel like doing it. This is
> a completely unessarry change and it makes it harder for me to review.
> 
> Can you go thorugh the complete patch and make sure to undo all
> similar changes, there are more of them.
> 
Sorry. I will fix in next version.

> >         } else {
> > -               writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
> > -               writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> > +               writel(host->saved_tune_para.iocon,
> > +                      host->base + MSDC_IOCON);
> > +               writel(host->saved_tune_para.pad_tune,
> > +                      host->base + MSDC_PAD_TUNE);
> > +               writel(host->saved_tune_para.pad_cmd_tune,
> > +                      host->base + PAD_CMD_TUNE);
> >         }
> >
> > +       if (timing == MMC_TIMING_MMC_HS400)
> > +               sdr_set_field(host->base + PAD_CMD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs400_cmd_int_delay);
> >         dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
> >  }
> >
> > @@ -1302,7 +1324,8 @@ static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
> >                         len_final = len;
> >                 }
> >                 start += len ? len : 1;
> > -               if (len >= 8 && start_final < 4)
> > +               if (len >= ENOUGH_MARGIN_MIN &&
> > +                   start_final < PREFER_START_POS_MAX)
> >                         break;
> >         }
> >
> > @@ -1325,48 +1348,128 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
> >         struct msdc_host *host = mmc_priv(mmc);
> >         u32 rise_delay = 0, fall_delay = 0;
> >         struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
> > +       struct msdc_delay_phase internal_delay_phase;
> >         u8 final_delay, final_maxlen;
> > +       u32 internal_delay = 0;
> >         int cmd_err;
> > -       int i;
> > +       int i, j;
> >
> > +       if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> > +           mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs200_cmd_int_delay);
> >         sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> >         for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> >                 sdr_set_field(host->base + MSDC_PAD_TUNE,
> >                               MSDC_PAD_TUNE_CMDRDLY, i);
> > -               mmc_send_tuning(mmc, opcode, &cmd_err);
> > -               if (!cmd_err)
> > -                       rise_delay |= (1 << i);
> > +               for (j = 0; j < 3; j++) {
> 
> Any reason to why looping three times makes sense? Maybe add a comment?
> 
Using the same parameters, it may sometimes pass the test,
but sometimes it may fail. To make sure the parameters is 
more stable, we test 3 times for on set of parameters.

Anyway, I will add comment here in next version.

> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               rise_delay |= (1 << i);
> > +                       } else {
> > +                               rise_delay &= ~(1 << i);
> > +                               break;
> > +                       }
> > +               }
> >         }
> >         final_rise_delay = get_best_delay(host, rise_delay);
> >         /* if rising edge has enough margin, then do not scan falling edge */
> > -       if (final_rise_delay.maxlen >= 10 ||
> > -           (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> > +       if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> > +           final_rise_delay.start < PREFER_START_POS_MAX)
> 
> This looks like clean-ups, as you are converting from magic numbers to
> defines. Please make this kind of changes separately.
I will drop this change in next version.

> 
> >                 goto skip_fall;
> >
> >         sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> >         for (i = 0; i < PAD_DELAY_MAX; i++) {
> >                 sdr_set_field(host->base + MSDC_PAD_TUNE,
> >                               MSDC_PAD_TUNE_CMDRDLY, i);
> > -               mmc_send_tuning(mmc, opcode, &cmd_err);
> > -               if (!cmd_err)
> > -                       fall_delay |= (1 << i);
> > +               for (j = 0; j < 3; j++) {
> 
> 3?
> 
> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               fall_delay |= (1 << i);
> > +                       } else {
> > +                               fall_delay &= ~(1 << i);
> > +                               break;
> > +                       };
> > +               }
> >         }
> >         final_fall_delay = get_best_delay(host, fall_delay);
> >
> >  skip_fall:
> >         final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
> > +       if (final_fall_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> > +           final_fall_delay.start < PREFER_START_POS_MAX)
> > +               final_maxlen = final_fall_delay.maxlen;
> >         if (final_maxlen == final_rise_delay.maxlen) {
> >                 sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> > -               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRDLY,
> >                               final_rise_delay.final_phase);
> >                 final_delay = final_rise_delay.final_phase;
> >         } else {
> >                 sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> > -               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRDLY,
> >                               final_fall_delay.final_phase);
> >                 final_delay = final_fall_delay.final_phase;
> >         }
> > +       if (host->hs200_cmd_int_delay)
> > +               goto skip_internal;
> >
> > +       for (i = 0; i < PAD_DELAY_MAX; i++) {
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY, i);
> > +               mmc_send_tuning(mmc, opcode, &cmd_err);
> > +               if (!cmd_err)
> > +                       internal_delay |= (1 << i);
> > +       }
> > +       dev_info(host->dev, "Final internal delay: 0x%x\n", internal_delay);
> 
> I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
> anything at all.
> 
Yes. We should use dev_dbg() here. Will be fixed in next version.

> > +       internal_delay_phase = get_best_delay(host, internal_delay);
> > +       sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
> > +                     internal_delay_phase.final_phase);
> > +skip_internal:
> > +       dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
> 
> I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
> anything at all.
> 
Yes. We should use dev_dbg() here. Will be fixed in next version.

> > +       return final_delay == 0xff ? -EIO : 0;
> > +}
> > +
> > +static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
> > +{
> > +       struct msdc_host *host = mmc_priv(mmc);
> > +       u32 cmd_delay = 0;
> > +       struct msdc_delay_phase final_cmd_delay = { 0,};
> > +       u8 final_delay;
> > +       int cmd_err;
> > +       int i, j;
> > +
> > +       /* select EMMC50 PAD CMD tune */
> > +       sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
> > +
> > +       if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> > +           mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs200_cmd_int_delay);
> > +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL,
> > +                     host->hs200_cmd_resp_sel);
> > +       for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> > +               sdr_set_field(host->base + PAD_CMD_TUNE,
> > +                             PAD_CMD_TUNE_RX_DLY3, i);
> > +               for (j = 0; j < 3; j++) {
> 
> 3?
> 
> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               cmd_delay |= (1 << i);
> > +                       } else {
> > +                               cmd_delay &= ~(1 << i);
> > +                               break;
> > +                       }
> > +               }
> > +       }
> > +       final_cmd_delay = get_best_delay(host, cmd_delay);
> > +       sdr_set_field(host->base + PAD_CMD_TUNE, PAD_CMD_TUNE_RX_DLY3,
> > +                     final_cmd_delay.final_phase);
> > +       final_delay = final_cmd_delay.final_phase;
> > +
> > +       dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
> 
> dev_info() -> dev_dbg() or remove it.
We will use dev_dbg() instead of dev_info(). Will be fixed in next
version.

> 
> >         return final_delay == 0xff ? -EIO : 0;
> >  }
> >
> > @@ -1389,7 +1492,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> >         }
> >         final_rise_delay = get_best_delay(host, rise_delay);
> >         /* if rising edge has enough margin, then do not scan falling edge */
> > -       if (final_rise_delay.maxlen >= 10 ||
> > +       if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN ||
> 
> Clean up. Move to separate change.
> 
Will drop it in current change.

> >             (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> >                 goto skip_fall;
> >
> > @@ -1422,6 +1525,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> >                 final_delay = final_fall_delay.final_phase;
> >         }
> >
> > +       dev_info(host->dev, "Final data pad delay: %x\n", final_delay);
> 
> dev_info() -> dev_dbg() or remove it.
> 
We will use dev_dbg() instead of dev_info(). Will be fixed in next
version.

> >         return final_delay == 0xff ? -EIO : 0;
> >  }
> >
> > @@ -1430,10 +1534,13 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> >         struct msdc_host *host = mmc_priv(mmc);
> >         int ret;
> >
> > +       if (host->hs400_mode)
> > +               ret = hs400_tune_response(mmc, opcode);
> > +       else
> >         ret = msdc_tune_response(mmc, opcode);
> 
> Because of the new else clause, seems like above needs an intendation.
> 
Sorry. Will be fixed in next version.

> >         if (ret == -EIO) {
> >                 dev_err(host->dev, "Tune response fail!\n");
> > -               return ret;
> > +               goto out;
> 
> Not needed, remove the label and this change.
> 
Will drop it in this change. 

> >         }
> >         if (host->hs400_mode == false) {
> >                 ret = msdc_tune_data(mmc, opcode);
> > @@ -1443,6 +1550,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> >
> >         host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
> >         host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
> > +       host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> > +out:
> >         return ret;
> >  }
> >
> > @@ -1553,6 +1662,18 @@ static int msdc_drv_probe(struct platform_device *pdev)
> >                 dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
> >                         host->hs400_ds_delay);
> >
> > +       if (!of_property_read_u32(pdev->dev.of_node, "hs200-cmd-int-delay",
> > +                                 &host->hs200_cmd_int_delay))
> > +               dev_dbg(&pdev->dev, "host->hs200-cmd-int-delay: %x\n",
> > +                       host->hs200_cmd_int_delay);
> > +       if (!of_property_read_u32(pdev->dev.of_node, "hs400-cmd-int-delay",
> > +                                 &host->hs400_cmd_int_delay))
> > +               dev_dbg(&pdev->dev, "host->hs400-cmd-int-delay: %x\n",
> > +                       host->hs400_cmd_int_delay);
> > +       if (!of_property_read_u32(pdev->dev.of_node, "cmd-resp-sel",
> > +                                 &host->hs200_cmd_resp_sel))
> > +               dev_dbg(&pdev->dev, "host->hs200_cmd-resp-sel: %x\n",
> > +                       host->hs200_cmd_resp_sel);
> 
> I suggest you take the oppotunity to move the MTK DTS parsing into its
> own function, include the existing parsing of the "hs400-ds-delay".
> This improve the readablitity of the code.
> 
Thanks. We will move all of MTK DTS parsing into msdc_of_property_parse
function.

> >         host->dev = &pdev->dev;
> >         host->mmc = mmc;
> >         host->src_clk_freq = clk_get_rate(host->src_clk);
> > @@ -1663,6 +1784,7 @@ static void msdc_save_reg(struct msdc_host *host)
> >         host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
> >         host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
> >         host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
> > +       host->save_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> >         host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
> >  }
> >
> > @@ -1675,6 +1797,7 @@ static void msdc_restore_reg(struct msdc_host *host)
> >         writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
> >         writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
> >         writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
> > +       writel(host->save_para.pad_cmd_tune, host->base + PAD_CMD_TUNE);
> >         writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
> >  }
> >
> > --
> > 1.7.9.5
> >
> 
> Kind regards
> Uffe
Best Regards,
Yong Mao.

WARNING: multiple messages have this Message-ID (diff)
From: yong.mao@mediatek.com (Yong Mao)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/2] mmc: mediatek: Use data tune for CMD line tune
Date: Fri, 13 Jan 2017 18:17:42 +0800	[thread overview]
Message-ID: <1484302662.10824.18.camel@mhfsdcap03> (raw)
In-Reply-To: <CAPDyKFq0_WpCAAmUDfaKApq7=tdpjNqL896FC=QPfGgE9JaEHA@mail.gmail.com>

On Thu, 2017-01-12 at 11:39 +0100, Ulf Hansson wrote:
> On 12 January 2017 at 11:04, Yong Mao <yong.mao@mediatek.com> wrote:
> > From: yong mao <yong.mao@mediatek.com>
> >
> > CMD response CRC error may cause cannot boot up
> > Change to use data tune for CMD line
> > Separate cmd internal delay for HS200/HS400 mode
> 
> Please try to work a little bit on improving the change log. Moreover
> as this is a fix for a regression (it seems like so?), please try to
> make that clear.
This change can fix CMD respose CRC issue in our platform.
I will try to make it clear in next version.

> 
> >
> > Signed-off-by: Yong Mao <yong.mao@mediatek.com>
> > Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> > ---
> >  arch/arm64/boot/dts/mediatek/mt8173-evb.dts |    3 +
> 
> Changes to the DTS files should be a separate change. Please split it
> into its own patch.
> 
> >  drivers/mmc/host/mtk-sd.c                   |  169 +++++++++++++++++++++++----
> >  2 files changed, 149 insertions(+), 23 deletions(-)
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > index 0ecaad4..29c3100 100644
> > --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> > @@ -134,6 +134,9 @@
> >         bus-width = <8>;
> >         max-frequency = <50000000>;
> >         cap-mmc-highspeed;
> > +       hs200-cmd-int-delay = <26>;
> > +       hs400-cmd-int-delay = <14>;
> > +       cmd-resp-sel = <0>; /* 0: rising, 1: falling */
> >         vmmc-supply = <&mt6397_vemc_3v3_reg>;
> >         vqmmc-supply = <&mt6397_vio18_reg>;
> >         non-removable;
> > diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> > index 80ba034..93eb395 100644
> > --- a/drivers/mmc/host/mtk-sd.c
> > +++ b/drivers/mmc/host/mtk-sd.c
> > @@ -75,6 +75,7 @@
> >  #define MSDC_PATCH_BIT1  0xb4
> >  #define MSDC_PAD_TUNE    0xec
> >  #define PAD_DS_TUNE      0x188
> > +#define PAD_CMD_TUNE     0x18c
> >  #define EMMC50_CFG0      0x208
> >
> >  /*--------------------------------------------------------------------------*/
> > @@ -210,12 +211,17 @@
> >  #define MSDC_PATCH_BIT_SPCPUSH    (0x1 << 29)  /* RW */
> >  #define MSDC_PATCH_BIT_DECRCTMO   (0x1 << 30)  /* RW */
> >
> > -#define MSDC_PAD_TUNE_DATRRDLY   (0x1f <<  8)  /* RW */
> > -#define MSDC_PAD_TUNE_CMDRDLY    (0x1f << 16)  /* RW */
> > +#define MSDC_PAD_TUNE_DATWRDLY    (0x1f <<  0)  /* RW */
> > +#define MSDC_PAD_TUNE_DATRRDLY    (0x1f <<  8) /* RW */
> > +#define MSDC_PAD_TUNE_CMDRDLY     (0x1f << 16)  /* RW */
> > +#define MSDC_PAD_TUNE_CMDRRDLY    (0x1f << 22)  /* RW */
> 
> Is there a white space change somewhere here? I don't see any changes
> to MSDC_PAD_TUNE_DATRRDLY and MSDC_PAD_TUNE_CMDRDLY.
> 
Sorry. I will fix in next version.

> > +#define MSDC_PAD_TUNE_CLKTDLY     (0x1f << 27)  /* RW */
> >
> > -#define PAD_DS_TUNE_DLY1         (0x1f << 2)   /* RW */
> > -#define PAD_DS_TUNE_DLY2         (0x1f << 7)   /* RW */
> > -#define PAD_DS_TUNE_DLY3         (0x1f << 12)  /* RW */
> > +#define PAD_DS_TUNE_DLY1          (0x1f << 2)   /* RW */
> > +#define PAD_DS_TUNE_DLY2          (0x1f << 7)   /* RW */
> > +#define PAD_DS_TUNE_DLY3          (0x1f << 12)  /* RW */
> > +
> 
> Ditto.
Ditto.

> 
> > +#define PAD_CMD_TUNE_RX_DLY3      (0x1f << 1)   /* RW */
> >
> >  #define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0)   /* RW */
> >  #define EMMC50_CFG_CRCSTS_EDGE    (0x1 << 3)   /* RW */
> > @@ -236,7 +242,9 @@
> >  #define CMD_TIMEOUT         (HZ/10 * 5)        /* 100ms x5 */
> >  #define DAT_TIMEOUT         (HZ    * 5)        /* 1000ms x5 */
> >
> > -#define PAD_DELAY_MAX  32 /* PAD delay cells */
> > +#define PAD_DELAY_MAX            32 /* PAD delay cells */
> 
> Ditto.
> 
Ditto.

> > +#define ENOUGH_MARGIN_MIN        12 /* Enough Margin */
> > +#define PREFER_START_POS_MAX     4  /* Prefer start position */
> >  /*--------------------------------------------------------------------------*/
> >  /* Descriptor Structure                                                     */
> >  /*--------------------------------------------------------------------------*/
> > @@ -284,12 +292,14 @@ struct msdc_save_para {
> >         u32 patch_bit0;
> >         u32 patch_bit1;
> >         u32 pad_ds_tune;
> > +       u32 pad_cmd_tune;
> >         u32 emmc50_cfg0;
> >  };
> >
> >  struct msdc_tune_para {
> >         u32 iocon;
> >         u32 pad_tune;
> > +       u32 pad_cmd_tune;
> >  };
> >
> >  struct msdc_delay_phase {
> > @@ -331,6 +341,9 @@ struct msdc_host {
> >         unsigned char timing;
> >         bool vqmmc_enabled;
> >         u32 hs400_ds_delay;
> > +       u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
> > +       u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
> > +       u32 hs200_cmd_resp_sel; /* cmd response sample selection */
> >         bool hs400_mode;        /* current eMMC will run at hs400 mode */
> >         struct msdc_save_para save_para; /* used when gate HCLK */
> >         struct msdc_tune_para def_tune_para; /* default tune setting */
> > @@ -596,12 +609,21 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
> >          */
> >         if (host->sclk <= 52000000) {
> >                 writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
> > -               writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> > +               writel(host->def_tune_para.pad_tune,
> > +                      host->base + MSDC_PAD_TUNE);
> 
> Please don't change code just because you feel like doing it. This is
> a completely unessarry change and it makes it harder for me to review.
> 
> Can you go thorugh the complete patch and make sure to undo all
> similar changes, there are more of them.
> 
Sorry. I will fix in next version.

> >         } else {
> > -               writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
> > -               writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> > +               writel(host->saved_tune_para.iocon,
> > +                      host->base + MSDC_IOCON);
> > +               writel(host->saved_tune_para.pad_tune,
> > +                      host->base + MSDC_PAD_TUNE);
> > +               writel(host->saved_tune_para.pad_cmd_tune,
> > +                      host->base + PAD_CMD_TUNE);
> >         }
> >
> > +       if (timing == MMC_TIMING_MMC_HS400)
> > +               sdr_set_field(host->base + PAD_CMD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs400_cmd_int_delay);
> >         dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
> >  }
> >
> > @@ -1302,7 +1324,8 @@ static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
> >                         len_final = len;
> >                 }
> >                 start += len ? len : 1;
> > -               if (len >= 8 && start_final < 4)
> > +               if (len >= ENOUGH_MARGIN_MIN &&
> > +                   start_final < PREFER_START_POS_MAX)
> >                         break;
> >         }
> >
> > @@ -1325,48 +1348,128 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
> >         struct msdc_host *host = mmc_priv(mmc);
> >         u32 rise_delay = 0, fall_delay = 0;
> >         struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
> > +       struct msdc_delay_phase internal_delay_phase;
> >         u8 final_delay, final_maxlen;
> > +       u32 internal_delay = 0;
> >         int cmd_err;
> > -       int i;
> > +       int i, j;
> >
> > +       if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> > +           mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs200_cmd_int_delay);
> >         sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> >         for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> >                 sdr_set_field(host->base + MSDC_PAD_TUNE,
> >                               MSDC_PAD_TUNE_CMDRDLY, i);
> > -               mmc_send_tuning(mmc, opcode, &cmd_err);
> > -               if (!cmd_err)
> > -                       rise_delay |= (1 << i);
> > +               for (j = 0; j < 3; j++) {
> 
> Any reason to why looping three times makes sense? Maybe add a comment?
> 
Using the same parameters, it may sometimes pass the test,
but sometimes it may fail. To make sure the parameters is 
more stable, we test 3 times for on set of parameters.

Anyway, I will add comment here in next version.

> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               rise_delay |= (1 << i);
> > +                       } else {
> > +                               rise_delay &= ~(1 << i);
> > +                               break;
> > +                       }
> > +               }
> >         }
> >         final_rise_delay = get_best_delay(host, rise_delay);
> >         /* if rising edge has enough margin, then do not scan falling edge */
> > -       if (final_rise_delay.maxlen >= 10 ||
> > -           (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> > +       if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> > +           final_rise_delay.start < PREFER_START_POS_MAX)
> 
> This looks like clean-ups, as you are converting from magic numbers to
> defines. Please make this kind of changes separately.
I will drop this change in next version.

> 
> >                 goto skip_fall;
> >
> >         sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> >         for (i = 0; i < PAD_DELAY_MAX; i++) {
> >                 sdr_set_field(host->base + MSDC_PAD_TUNE,
> >                               MSDC_PAD_TUNE_CMDRDLY, i);
> > -               mmc_send_tuning(mmc, opcode, &cmd_err);
> > -               if (!cmd_err)
> > -                       fall_delay |= (1 << i);
> > +               for (j = 0; j < 3; j++) {
> 
> 3?
> 
> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               fall_delay |= (1 << i);
> > +                       } else {
> > +                               fall_delay &= ~(1 << i);
> > +                               break;
> > +                       };
> > +               }
> >         }
> >         final_fall_delay = get_best_delay(host, fall_delay);
> >
> >  skip_fall:
> >         final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
> > +       if (final_fall_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> > +           final_fall_delay.start < PREFER_START_POS_MAX)
> > +               final_maxlen = final_fall_delay.maxlen;
> >         if (final_maxlen == final_rise_delay.maxlen) {
> >                 sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> > -               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRDLY,
> >                               final_rise_delay.final_phase);
> >                 final_delay = final_rise_delay.final_phase;
> >         } else {
> >                 sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> > -               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRDLY,
> >                               final_fall_delay.final_phase);
> >                 final_delay = final_fall_delay.final_phase;
> >         }
> > +       if (host->hs200_cmd_int_delay)
> > +               goto skip_internal;
> >
> > +       for (i = 0; i < PAD_DELAY_MAX; i++) {
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY, i);
> > +               mmc_send_tuning(mmc, opcode, &cmd_err);
> > +               if (!cmd_err)
> > +                       internal_delay |= (1 << i);
> > +       }
> > +       dev_info(host->dev, "Final internal delay: 0x%x\n", internal_delay);
> 
> I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
> anything at all.
> 
Yes. We should use dev_dbg() here. Will be fixed in next version.

> > +       internal_delay_phase = get_best_delay(host, internal_delay);
> > +       sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
> > +                     internal_delay_phase.final_phase);
> > +skip_internal:
> > +       dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
> 
> I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
> anything at all.
> 
Yes. We should use dev_dbg() here. Will be fixed in next version.

> > +       return final_delay == 0xff ? -EIO : 0;
> > +}
> > +
> > +static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
> > +{
> > +       struct msdc_host *host = mmc_priv(mmc);
> > +       u32 cmd_delay = 0;
> > +       struct msdc_delay_phase final_cmd_delay = { 0,};
> > +       u8 final_delay;
> > +       int cmd_err;
> > +       int i, j;
> > +
> > +       /* select EMMC50 PAD CMD tune */
> > +       sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
> > +
> > +       if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> > +           mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> > +               sdr_set_field(host->base + MSDC_PAD_TUNE,
> > +                             MSDC_PAD_TUNE_CMDRRDLY,
> > +                             host->hs200_cmd_int_delay);
> > +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL,
> > +                     host->hs200_cmd_resp_sel);
> > +       for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> > +               sdr_set_field(host->base + PAD_CMD_TUNE,
> > +                             PAD_CMD_TUNE_RX_DLY3, i);
> > +               for (j = 0; j < 3; j++) {
> 
> 3?
> 
> > +                       mmc_send_tuning(mmc, opcode, &cmd_err);
> > +                       if (!cmd_err) {
> > +                               cmd_delay |= (1 << i);
> > +                       } else {
> > +                               cmd_delay &= ~(1 << i);
> > +                               break;
> > +                       }
> > +               }
> > +       }
> > +       final_cmd_delay = get_best_delay(host, cmd_delay);
> > +       sdr_set_field(host->base + PAD_CMD_TUNE, PAD_CMD_TUNE_RX_DLY3,
> > +                     final_cmd_delay.final_phase);
> > +       final_delay = final_cmd_delay.final_phase;
> > +
> > +       dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
> 
> dev_info() -> dev_dbg() or remove it.
We will use dev_dbg() instead of dev_info(). Will be fixed in next
version.

> 
> >         return final_delay == 0xff ? -EIO : 0;
> >  }
> >
> > @@ -1389,7 +1492,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> >         }
> >         final_rise_delay = get_best_delay(host, rise_delay);
> >         /* if rising edge has enough margin, then do not scan falling edge */
> > -       if (final_rise_delay.maxlen >= 10 ||
> > +       if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN ||
> 
> Clean up. Move to separate change.
> 
Will drop it in current change.

> >             (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> >                 goto skip_fall;
> >
> > @@ -1422,6 +1525,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> >                 final_delay = final_fall_delay.final_phase;
> >         }
> >
> > +       dev_info(host->dev, "Final data pad delay: %x\n", final_delay);
> 
> dev_info() -> dev_dbg() or remove it.
> 
We will use dev_dbg() instead of dev_info(). Will be fixed in next
version.

> >         return final_delay == 0xff ? -EIO : 0;
> >  }
> >
> > @@ -1430,10 +1534,13 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> >         struct msdc_host *host = mmc_priv(mmc);
> >         int ret;
> >
> > +       if (host->hs400_mode)
> > +               ret = hs400_tune_response(mmc, opcode);
> > +       else
> >         ret = msdc_tune_response(mmc, opcode);
> 
> Because of the new else clause, seems like above needs an intendation.
> 
Sorry. Will be fixed in next version.

> >         if (ret == -EIO) {
> >                 dev_err(host->dev, "Tune response fail!\n");
> > -               return ret;
> > +               goto out;
> 
> Not needed, remove the label and this change.
> 
Will drop it in this change. 

> >         }
> >         if (host->hs400_mode == false) {
> >                 ret = msdc_tune_data(mmc, opcode);
> > @@ -1443,6 +1550,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> >
> >         host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
> >         host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
> > +       host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> > +out:
> >         return ret;
> >  }
> >
> > @@ -1553,6 +1662,18 @@ static int msdc_drv_probe(struct platform_device *pdev)
> >                 dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
> >                         host->hs400_ds_delay);
> >
> > +       if (!of_property_read_u32(pdev->dev.of_node, "hs200-cmd-int-delay",
> > +                                 &host->hs200_cmd_int_delay))
> > +               dev_dbg(&pdev->dev, "host->hs200-cmd-int-delay: %x\n",
> > +                       host->hs200_cmd_int_delay);
> > +       if (!of_property_read_u32(pdev->dev.of_node, "hs400-cmd-int-delay",
> > +                                 &host->hs400_cmd_int_delay))
> > +               dev_dbg(&pdev->dev, "host->hs400-cmd-int-delay: %x\n",
> > +                       host->hs400_cmd_int_delay);
> > +       if (!of_property_read_u32(pdev->dev.of_node, "cmd-resp-sel",
> > +                                 &host->hs200_cmd_resp_sel))
> > +               dev_dbg(&pdev->dev, "host->hs200_cmd-resp-sel: %x\n",
> > +                       host->hs200_cmd_resp_sel);
> 
> I suggest you take the oppotunity to move the MTK DTS parsing into its
> own function, include the existing parsing of the "hs400-ds-delay".
> This improve the readablitity of the code.
> 
Thanks. We will move all of MTK DTS parsing into msdc_of_property_parse
function.

> >         host->dev = &pdev->dev;
> >         host->mmc = mmc;
> >         host->src_clk_freq = clk_get_rate(host->src_clk);
> > @@ -1663,6 +1784,7 @@ static void msdc_save_reg(struct msdc_host *host)
> >         host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
> >         host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
> >         host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
> > +       host->save_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> >         host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
> >  }
> >
> > @@ -1675,6 +1797,7 @@ static void msdc_restore_reg(struct msdc_host *host)
> >         writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
> >         writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
> >         writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
> > +       writel(host->save_para.pad_cmd_tune, host->base + PAD_CMD_TUNE);
> >         writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
> >  }
> >
> > --
> > 1.7.9.5
> >
> 
> Kind regards
> Uffe
Best Regards,
Yong Mao.

  reply	other threads:[~2017-01-13 10:18 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-12 10:04 [PATCH 0/2] Use data tune for CMD line tune Yong Mao
2017-01-12 10:04 ` Yong Mao
2017-01-12 10:04 ` Yong Mao
2017-01-12 10:04 ` [PATCH 1/2] mmc: mediatek: " Yong Mao
2017-01-12 10:04   ` Yong Mao
2017-01-12 10:04   ` Yong Mao
2017-01-12 10:39   ` Ulf Hansson
2017-01-12 10:39     ` Ulf Hansson
2017-01-12 10:39     ` Ulf Hansson
2017-01-13 10:17     ` Yong Mao [this message]
2017-01-13 10:17       ` Yong Mao
2017-01-13 10:17       ` Yong Mao
2017-01-12 10:04 ` [PATCH 2/2] mmc: dt-bindings: update Mediatek MMC bindings Yong Mao
2017-01-12 10:04   ` Yong Mao
2017-01-12 10:04   ` Yong Mao
2017-01-12 10:15   ` Ulf Hansson
2017-01-18 21:34   ` Rob Herring
2017-01-18 21:34     ` Rob Herring
2017-01-18 21:34     ` Rob Herring

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=1484302662.10824.18.camel@mhfsdcap03 \
    --to=yong.mao@mediatek.com \
    --cc=catalin.marinas@arm.com \
    --cc=chaotian.jing@mediatek.com \
    --cc=chunfeng.yun@mediatek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dianders@chromium.org \
    --cc=drinkcat@chromium.org \
    --cc=eddie.huang@mediatek.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=javier@osg.samsung.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=matthias.bgg@gmail.com \
    --cc=p.zabel@pengutronix.de \
    --cc=robh+dt@kernel.org \
    --cc=srv_heupstream@mediatek.com \
    --cc=ulf.hansson@linaro.org \
    --cc=will.deacon@arm.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.