Re: [PATCH V12 18/23] mmc: sdhci-uhs2: add request() and others

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 15 Sept 2023 at 11:44, Victor Shih <victorshihgli@xxxxxxxxx> wrote:
>
> From: Victor Shih <victor.shih@xxxxxxxxxxxxxxxxxxx>
>
> This is a sdhci version of mmc's request operation.
> It covers both UHS-I and UHS-II.

Okay, but again, please elaborate on why we need/want this.

>
> Signed-off-by: Ben Chuang <ben.chuang@xxxxxxxxxxxxxxxxxxx>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@xxxxxxxxxx>
> Signed-off-by: Victor Shih <victor.shih@xxxxxxxxxxxxxxxxxxx>
> ---
>
> Updates in V11:
>  - Drop the check mmc_card_uhs2_hd_mode(host->mmc)
>    in sdhci_uhs2_set_transfer_mode().
>
> Updates in V10:
>  - Use tmode_half_duplex to instead of uhs2_tmode0_flag
>    in sdhci_uhs2_set_transfer_mode().
>
> Updates in V9:
>  - Modify the annotations in __sdhci_uhs2_send_command().
>
> Updates in V8:
>  - Adjust the position of matching brackets in
>    sdhci_uhs2_send_command_retry().
>  - Modify CameCase definition in __sdhci_uhs2_finish_command().
>  - Modify error message in __sdhci_uhs2_finish_command().
>  - sdhci_uhs2_send_command_retry() to instead of sdhci_uhs2_send_command()
>    in sdhci_uhs2_request().
>  - Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_request_atomic().
>  - Add forward declaration for sdhci_send_command().
>
> Updates in V7:
>  - Cancel export state of some functions.
>  - Remove unnecessary whitespace changes.
>
> Updates in V6:
>  - Add uhs2_dev_cmd() to simplify code.
>  - Remove unnecessary functions.
>  - Cancel export state of some functions.
>  - Drop use CONFIG_MMC_DEBUG().
>  - Wrap at 100 columns in some functions.
>
> ---
>
>  drivers/mmc/host/sdhci-uhs2.c | 412 ++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/sdhci.c      |  49 ++--
>  drivers/mmc/host/sdhci.h      |   8 +
>  3 files changed, 454 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 09b86fec9f7b..1f8d527424fd 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c

[...]

> +
> +static void __sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> +{
> +       int i, j;
> +       int cmd_reg;
> +
> +       i = 0;
> +       sdhci_writel(host,
> +                    ((u32)cmd->uhs2_cmd->arg << 16) |
> +                               (u32)cmd->uhs2_cmd->header,
> +                    SDHCI_UHS2_CMD_PACKET + i);
> +       i += 4;
> +
> +       /*
> +        * Per spec, payload (config) should be MSB before sending out.
> +        * But we don't need convert here because had set payload as
> +        * MSB when preparing config read/write commands.
> +        */
> +       for (j = 0; j < cmd->uhs2_cmd->payload_len / sizeof(u32); j++) {
> +               sdhci_writel(host, *(cmd->uhs2_cmd->payload + j), SDHCI_UHS2_CMD_PACKET + i);
> +               i += 4;
> +       }
> +
> +       for ( ; i < SDHCI_UHS2_CMD_PACK_MAX_LEN; i += 4)
> +               sdhci_writel(host, 0, SDHCI_UHS2_CMD_PACKET + i);
> +
> +       DBG("UHS2 CMD packet_len = %d.\n", cmd->uhs2_cmd->packet_len);
> +       for (i = 0; i < cmd->uhs2_cmd->packet_len; i++)
> +               DBG("UHS2 CMD_PACKET[%d] = 0x%x.\n", i,
> +                   sdhci_readb(host, SDHCI_UHS2_CMD_PACKET + i));

We are ignoring what we just read. Isn't there something we need to verify?

Moreover, the whole thing with i,j and the +4 thing above looks a bit
odd to me. I am not sure whether the above can be simplified, but I
leave that for you to have a second look at.

> +
> +       cmd_reg = FIELD_PREP(SDHCI_UHS2_CMD_PACK_LEN_MASK, cmd->uhs2_cmd->packet_len);
> +       if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
> +               cmd_reg |= SDHCI_UHS2_CMD_DATA;
> +       if (cmd->opcode == MMC_STOP_TRANSMISSION)
> +               cmd_reg |= SDHCI_UHS2_CMD_CMD12;
> +
> +       /* UHS2 Native ABORT */
> +       if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> +           (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT))
> +               cmd_reg |= SDHCI_UHS2_CMD_TRNS_ABORT;
> +
> +       /* UHS2 Native DORMANT */
> +       if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> +           (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_GO_DORMANT_STATE))
> +               cmd_reg |= SDHCI_UHS2_CMD_DORMANT;
> +
> +       DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
> +
> +       sdhci_writew(host, cmd_reg, SDHCI_UHS2_CMD);
> +}

[...]

> +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
> +                                         struct mmc_command *cmd,
> +                                         unsigned long flags)
> +       __releases(host->lock)
> +       __acquires(host->lock)
> +{
> +       struct mmc_command *deferred_cmd = host->deferred_cmd;
> +       int timeout = 10; /* Approx. 10 ms */
> +       bool present;

Why do we need a retry mechanism at this level? The mmc core sometimes
retries commands when it seems reasonable, why isn't that sufficient?

> +
> +       while (!sdhci_uhs2_send_command(host, cmd)) {
> +               if (!timeout--) {
> +                       pr_err("%s: Controller never released inhibit bit(s).\n",
> +                              mmc_hostname(host->mmc));
> +                       sdhci_dumpregs(host);
> +                       cmd->error = -EIO;
> +                       return false;
> +               }
> +
> +               spin_unlock_irqrestore(&host->lock, flags);
> +
> +               usleep_range(1000, 1250);
> +
> +               present = host->mmc->ops->get_cd(host->mmc);
> +
> +               spin_lock_irqsave(&host->lock, flags);
> +
> +               /* A deferred command might disappear, handle that */
> +               if (cmd == deferred_cmd && cmd != host->deferred_cmd)
> +                       return true;
> +
> +               if (sdhci_present_error(host, cmd, present))
> +                       return false;
> +       }

If the retry is needed, would it be possible to convert into using
read_poll_timeout() for the above while loop instead? If so, please
make that conversion.

> +
> +       if (cmd == host->deferred_cmd)
> +               host->deferred_cmd = NULL;
> +
> +       return true;
> +}
> +
> +static void __sdhci_uhs2_finish_command(struct sdhci_host *host)
> +{
> +       struct mmc_command *cmd = host->cmd;
> +       u8 resp;
> +       u8 ecode;
> +       bool breada0 = 0;

Nitpick: Maybe find some better variable names. Like error_code...

> +       int i;
> +
> +       if (host->mmc->flags & MMC_UHS2_SD_TRAN) {
> +               resp = sdhci_readb(host, SDHCI_UHS2_RESPONSE + 2);
> +               if (resp & UHS2_RES_NACK_MASK) {
> +                       ecode = (resp >> UHS2_RES_ECODE_POS) & UHS2_RES_ECODE_MASK;
> +                       pr_err("%s: NACK response, ECODE=0x%x.\n", mmc_hostname(host->mmc), ecode);
> +               }
> +               breada0 = 1;
> +       }
> +
> +       if (cmd->uhs2_resp &&
> +           cmd->uhs2_resp_len && cmd->uhs2_resp_len <= 20) {
> +               /* Get whole response of some native CCMD, like
> +                * DEVICE_INIT, ENUMERATE.
> +                */
> +               for (i = 0; i < cmd->uhs2_resp_len; i++)
> +                       cmd->uhs2_resp[i] = sdhci_readb(host, SDHCI_UHS2_RESPONSE + i);
> +       } else {
> +               /* Get SD CMD response and Payload for some read
> +                * CCMD, like INQUIRY_CFG.
> +                */
> +               /* Per spec (p136), payload field is divided into
> +                * a unit of DWORD and transmission order within
> +                * a DWORD is big endian.
> +                */
> +               if (!breada0)
> +                       sdhci_readl(host, SDHCI_UHS2_RESPONSE);
> +               for (i = 4; i < 20; i += 4) {

Again we do sdhci_readl above but just ignore the data. I assume
that's deliberate, as we are probably just interested in the remaining
pieces.

Moreover, the whole thing with +4 things continues to look a bit odd
to me. I am not sure whether it can be simplified, but I leave that
for you to have a second look at.

> +                       cmd->resp[i / 4 - 1] =
> +                               (sdhci_readb(host,
> +                                            SDHCI_UHS2_RESPONSE + i) << 24) |
> +                               (sdhci_readb(host,
> +                                            SDHCI_UHS2_RESPONSE + i + 1)
> +                                       << 16) |
> +                               (sdhci_readb(host,
> +                                            SDHCI_UHS2_RESPONSE + i + 2)
> +                                       << 8) |
> +                               sdhci_readb(host, SDHCI_UHS2_RESPONSE + i + 3);
> +               }
> +       }
> +}

[...]

> +
> +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)

static void

> +{
> +       struct sdhci_host *host = mmc_priv(mmc);
> +       struct mmc_command *cmd;
> +       unsigned long flags;
> +       bool present;
> +
> +       if (!(sdhci_uhs2_mode(host))) {
> +               sdhci_request(mmc, mrq);
> +               return;
> +       }
> +
> +       mrq->stop = NULL;
> +       mrq->sbc = NULL;
> +       if (mrq->data)
> +               mrq->data->stop = NULL;
> +
> +       /* Firstly check card presence */
> +       present = mmc->ops->get_cd(mmc);
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +
> +       if (sdhci_present_error(host, mrq->cmd, present))
> +               goto out_finish;
> +
> +       cmd = mrq->cmd;
> +
> +       if (!sdhci_uhs2_send_command_retry(host, cmd, flags))
> +               goto out_finish;
> +
> +       spin_unlock_irqrestore(&host->lock, flags);
> +
> +       return;
> +
> +out_finish:
> +       sdhci_finish_mrq(host, mrq);
> +       spin_unlock_irqrestore(&host->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request);

Drop this, it's not used outside this module.

> +
> +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)

This function is entirely unused. Did you actually test this with an
hsq enabled host? Or perhaps you have just added this for
completeness?

> +{
> +       struct sdhci_host *host = mmc_priv(mmc);
> +       struct mmc_command *cmd;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!sdhci_uhs2_mode(host))
> +               return sdhci_request_atomic(mmc, mrq);
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +
> +       if (sdhci_present_error(host, mrq->cmd, true)) {
> +               sdhci_finish_mrq(host, mrq);
> +               goto out_finish;
> +       }
> +
> +       cmd = mrq->cmd;
> +
> +       /*
> +        * The HSQ may send a command in interrupt context without polling
> +        * the busy signaling, which means we should return BUSY if controller
> +        * has not released inhibit bits to allow HSQ trying to send request
> +        * again in non-atomic context. So we should not finish this request
> +        * here.
> +        */
> +       if (!sdhci_uhs2_send_command(host, cmd))
> +               ret = -EBUSY;
> +
> +out_finish:
> +       spin_unlock_irqrestore(&host->lock, flags);
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
> +

[...]

Kind regards
Uffe



[Index of Archives]     [Linux Memonry Technology]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux