RE: [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization

Nath, Arindam
2011-02-18T11:33:54Z

Hi Zhangfei,

> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Friday, February 18, 2011 4:20 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 09/12] mmc: sd: add support for tuning during uhs
> initialization
>
> On Tue, Feb 15, 2011 at 4:35 AM, Arindam Nath <arindam.nath@amd.com>
> wrote:
> > Host Controller needs tuning during initialization to operate SDR50
> > and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> > indicated by bit 45 of the Host Controller Capabilities register.
> > A new command CMD19 has been defined in the Physical Layer spec v3.01
> > to request the card to send tuning pattern.
> >
> > We enable Buffer Read Ready interrupt at the very begining of tuning
> > procedure, because that is the only interrupt generated by the Host
> > Controller during tuning. Since there is no actual data transfer
> > between card and host memory, we don't set DMA Enable (bit 0) in the
> > Transfer Mode register. The tuning block is sent by the card to the
> > Host Controller using DAT lines, so we set Data Present Select (bit
> 5)
> > in the Command register. The Host Controller is responsible for doing
> > the verfication of tuning block sent by the card at the hardware
> level.
> > After sending CMD19, we wait for Buffer Read Ready interrupt. In case
> > we don't receive an interrupt after the specified timeout value, we
> > fall back on fixed sampling clock by setting Execute Tuning (bit 6)
> > and Sampling Clock Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> >  drivers/mmc/core/sd.c     |    4 +
> >  drivers/mmc/host/sdhci.c  |  142
> ++++++++++++++++++++++++++++++++++++++++++++-
> >  drivers/mmc/host/sdhci.h  |    3 +
> >  include/linux/mmc/host.h  |    1 +
> >  include/linux/mmc/mmc.h   |    1 +
> >  include/linux/mmc/sdhci.h |    4 +
> >  6 files changed, 153 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index c52d427..3113cb6 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -624,6 +624,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> >        /* Set current limit for the card */
> >        err = sd_set_current_limit(card, status);
> >
> > +       /* SPI mode doesn't define CMD19 */
> > +       if (!mmc_host_is_spi(card->host))
> > +               card->host->ops->execute_tuning(card->host);
> > +
> >  out:
> >        kfree(status);
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index fc2cba6..6f3f0dc 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> >  #define SDHCI_USE_LEDS_CLASS
> >  #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> >  static unsigned int debug_quirks = 0;
> >
> >  static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> > @@ -872,7 +874,10 @@ static void sdhci_set_transfer_mode(struct
> sdhci_host *host,
> >
> >        if (data->flags & MMC_DATA_READ)
> >                mode |= SDHCI_TRNS_READ;
> > -       if (host->flags & SDHCI_REQ_USE_DMA)
> > +
> > +       /* CMD19 requires DMA Enable to be set to 0 */
> > +       if ((host->flags & SDHCI_REQ_USE_DMA) &&
> > +          (host->cmd->opcode != MMC_SEND_TUNING_BLOCK))
> >                mode |= SDHCI_TRNS_DMA;
>
> Here may have no chance to execute, since sdhci_set_transfer_mode will
> return directly if data is NULL.

Thanks. Since we are setting data to NULL inside sdhci_execute_tuning() below, this condition check becomes redundant. I will remove this check in V2 of the patch.

>
> >
> >        sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> > @@ -988,7 +993,9 @@ static void sdhci_send_command(struct sdhci_host
> *host, struct mmc_command *cmd)
> >                flags |= SDHCI_CMD_CRC;
> >        if (cmd->flags & MMC_RSP_OPCODE)
> >                flags |= SDHCI_CMD_INDEX;
> > -       if (cmd->data)
> > +
> > +       /* CMD19 is special in that the Data Present Select should be
> set */
> > +       if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
> >                flags |= SDHCI_CMD_DATA;
> >
> >        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
> SDHCI_COMMAND);
> > @@ -1494,6 +1501,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> >        return max_current_180;
> >  }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > +       struct sdhci_host *host;
> > +       u16 ctrl;
> > +       int tuning_loop_counter = MAX_TUNING_LOOP;
> > +       unsigned long flags;
> > +       unsigned long timeout;
> > +
> > +       host = mmc_priv(mmc);
> > +
> > +       spin_lock_irqsave(&host->lock, flags);
> > +
> > +       ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +
> > +       /*
> > +        * Host Controller needs tuning only in case of SDR104 mode
> > +        * and for SDR50 mode when Use Tuning for SDR50 is set in
> > +        * Capabilities register.
> > +        */
> > +       if ((ctrl & SDHCI_CTRL_UHS_SDR104) ||
> > +           ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > +           (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > +               ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > +       else {
> > +               spin_unlock_irqrestore(&host->lock, flags);
> > +               return;
> > +       }
> > +
> > +       sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > +       /*
> > +        * As per the Host Controller spec v3.00, tuning command
> > +        * generates Buffer Read Ready interrupt, so enable that.
> > +        */
> > +       sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > +       /*
> > +        * Issue CMD19 repeatedly till Execute Tuning is set to 0 or
> the number
> > +        * of loops reaches 40 times or a timeout of 150ms occurs.
> > +        */
> > +       timeout = 150;
> > +       do {
> > +               struct mmc_command cmd;
> > +               struct mmc_request mrq;
> > +
> > +               if (!tuning_loop_counter && !timeout)
> > +                       break;
> > +
> > +               memset(&cmd, 0, sizeof(struct mmc_command));
> > +               cmd.opcode = MMC_SEND_TUNING_BLOCK;
> > +               cmd.arg = 0;
> > +               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> > +
> > +               memset(&cmd.resp, 0, sizeof(cmd.resp));
> > +               cmd.retries = 0;
> > +
> > +               cmd.data = NULL;
>
> Set cmd.data = null, then sdhci_set_transfer_mode will return.

Thanks. Will take care of that.

Regards,
Arindam