Re: [PATCH V6 06/24] mmc: core: Support UHS-II card control and access

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

 



Hi, Ulf

On Wed, Feb 8, 2023 at 11:31 PM Ulf Hansson <ulf.hansson@xxxxxxxxxx> wrote:
>
> "On Tue, 13 Dec 2022 at 10:01, Victor Shih <victorshihgli@xxxxxxxxx> wrote:
> >
> > Embed UHS-II access/control functionality into the MMC request
> > processing flow.
> >
> > Signed-off-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
> > Signed-off-by: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx>
> > Signed-off-by: Victor Shih <victor.shih@xxxxxxxxxxxxxxxxxxx>
> > ---
> >  drivers/mmc/core/block.c   |    6 +-
> >  drivers/mmc/core/core.c    |   20 +
> >  drivers/mmc/core/mmc_ops.c |   25 +-
> >  drivers/mmc/core/mmc_ops.h |    1 +
> >  drivers/mmc/core/sd.c      |   11 +-
> >  drivers/mmc/core/sd.h      |    3 +
> >  drivers/mmc/core/sd_ops.c  |   13 +
> >  drivers/mmc/core/sd_ops.h  |    3 +
> >  drivers/mmc/core/sd_uhs2.c | 1171 +++++++++++++++++++++++++++++++++++-
> >  9 files changed, 1206 insertions(+), 47 deletions(-)
> >
> > diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
> > index 20da7ed43e6d..d3e8ec43cdd5 100644
> > --- a/drivers/mmc/core/block.c
> > +++ b/drivers/mmc/core/block.c
> > @@ -1596,6 +1596,9 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
> >         struct request *req = mmc_queue_req_to_req(mqrq);
> >         struct mmc_blk_data *md = mq->blkdata;
> >         bool do_rel_wr, do_data_tag;
> > +       bool do_multi;
> > +
> > +       do_multi = (card->uhs2_state & MMC_UHS2_INITIALIZED) ? true : false;
> >
> >         mmc_blk_data_prep(mq, mqrq, recovery_mode, &do_rel_wr, &do_data_tag);
> >
> > @@ -1606,7 +1609,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
> >                 brq->cmd.arg <<= 9;
> >         brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
> >
> > -       if (brq->data.blocks > 1 || do_rel_wr) {
> > +       if (brq->data.blocks > 1 || do_rel_wr || do_multi) {
>
> This looks wrong to me. UHS2 can use single block read/writes too. Right?
>
> >                 /* SPI multiblock writes terminate using a special
> >                  * token, not a STOP_TRANSMISSION request.
> >                  */
> > @@ -1619,6 +1622,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
> >                 brq->mrq.stop = NULL;
> >                 readcmd = MMC_READ_SINGLE_BLOCK;
> >                 writecmd = MMC_WRITE_BLOCK;
> > +               brq->cmd.uhs2_tmode0_flag = 1;
>
> As "do_multi" is always set for UHS2, setting this flag here seems to
> be wrong/redundant.
>
> Anyway, if I understand correctly, the flag is intended to be used to
> inform the host driver whether the so-called 2L_HD_mode (half-duplex
> or full-duplex) should be used for the I/O request or not.
>
> To fix the above behaviour, I suggest we try to move the entire
> control of the flag into mmc_uhs2_prepare_cmd(). We want the flag to
> be set for multi block read/writes (CMD18 and CMD25), but only if the
> host and card supports the 2L_HD_mode too. According to my earlier
> suggestions, we should be able to check that via the bits we set
> earlier in the ios->timing.
>
> Moreover, by making mmc_uhs2_prepare_cmd() responsible for setting the
> flag, I think we can also move the definition of the flag into the
> struct uhs2_command. While at it, I suggest we also rename the flag
> into "tmode_half_duplex", to better describe its purpose, which also
> means the interpretation of the flag becomes inverted.
>
> >         }
> >         brq->cmd.opcode = rq_data_dir(req) == READ ? readcmd : writecmd;
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index ad1a40446813..01f8216885ad 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -334,6 +334,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
> >
> >  int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> >  {
> > +       struct uhs2_command uhs2_cmd;
> > +       __be32 payload[4]; /* for maximum size */
> >         int err;
> >
> >         init_completion(&mrq->cmd_completion);
> > @@ -351,6 +353,14 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 return err;
> >
> > +       if (host->card) {
> > +               if (host->card->uhs2_state & MMC_UHS2_INITIALIZED) {
> > +                       uhs2_cmd.payload = payload;
> > +                       mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > +                       mmc_uhs2_prepare_cmd(host, mrq);
> > +               }
> > +       }
>
> To avoid open coding, please move this into mmc_mrq_prep() or add a
> new helper function for it.
>

I will add a helper function in the sd_ops.h and update in V7 version.

> > +
> >         led_trigger_event(host->led, LED_FULL);
> >         __mmc_start_request(host, mrq);
> >
> > @@ -430,6 +440,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
> >   */
> >  int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> >  {
> > +       struct uhs2_command uhs2_cmd;
> > +       __be32 payload[4]; /* for maximum size */
> >         int err;
> >
> >         /*
> > @@ -450,6 +462,14 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 goto out_err;
> >
> > +       if (host->card) {
> > +               if (host->card->uhs2_state & MMC_UHS2_INITIALIZED) {
> > +                       uhs2_cmd.payload = payload;
> > +                       mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > +                       mmc_uhs2_prepare_cmd(host, mrq);
> > +               }
> > +       }
>
> Ditto.
>

I will add a helper function in the sd_ops.h and update in V7 version.

> > +
> >         err = host->cqe_ops->cqe_request(host, mrq);
> >         if (err)
> >                 goto out_err;
> > diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> > index 81c55bfd6e0c..daa1f4ccd99a 100644
> > --- a/drivers/mmc/core/mmc_ops.c
> > +++ b/drivers/mmc/core/mmc_ops.c
> > @@ -144,10 +144,24 @@ int mmc_set_dsr(struct mmc_host *host)
> >         return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
> >  }
> >
> > +int __mmc_go_idle(struct mmc_host *host)
> > +{
> > +       struct mmc_command cmd = {};
> > +       int err;
> > +
> > +       cmd.opcode = MMC_GO_IDLE_STATE;
> > +       cmd.arg = 0;
> > +       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       mmc_delay(1);
> > +
> > +       return err;
> > +}
> > +
> >  int mmc_go_idle(struct mmc_host *host)
> >  {
> >         int err;
> > -       struct mmc_command cmd = {};
> >
> >         /*
> >          * Non-SPI hosts need to prevent chipselect going active during
> > @@ -163,13 +177,7 @@ int mmc_go_idle(struct mmc_host *host)
> >                 mmc_delay(1);
> >         }
> >
> > -       cmd.opcode = MMC_GO_IDLE_STATE;
> > -       cmd.arg = 0;
> > -       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
> > -
> > -       err = mmc_wait_for_cmd(host, &cmd, 0);
> > -
> > -       mmc_delay(1);
> > +       err = __mmc_go_idle(host);
>
> The above changes for mmc_go_idle() are pure refactorings, please move
> these changes into a separate patch that precedes $subject patch.
>
> This will help to simplify the review.
>
> >
> >         if (!mmc_host_is_spi(host)) {
> >                 mmc_set_chip_select(host, MMC_CS_DONTCARE);
> > @@ -300,6 +308,7 @@ int mmc_send_adtc_data(struct mmc_card *card, struct mmc_host *host, u32 opcode,
> >          * not R1 plus a data block.
> >          */
> >         cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
> > +       cmd.uhs2_tmode0_flag = 1;
>
> As I stated above, I think we can drop this and rather manage the flag
> from mmc_uhs2_prepare_cmd() instead. From now on, I will stop to
> comment more on the use of the uhs2_tmode0_flag, as the similar
> comments applies to more places.
>
> [...]
>
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index 3ba7b3cf4652..29c802dec988 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -11,6 +11,7 @@
> >  #include <linux/types.h>
> >
> >  struct mmc_card;
> > +struct mmc_command;
>
> Looks like this should be "struct mmc_request;" instead.
>
> >  struct mmc_host;
> >
> >  int mmc_app_set_bus_width(struct mmc_card *card, int width);
> > @@ -19,10 +20,12 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
> >  int mmc_send_if_cond_pcie(struct mmc_host *host, u32 ocr);
> >  int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
> >  int mmc_app_send_scr(struct mmc_card *card);
> > +int mmc_decode_scr(struct mmc_card *card);
> >  int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> >         u8 value, u8 *resp);
> >  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> >  int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
> > +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
> >
> >  #endif
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index 800957f74632..a79eb08ec540 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -1,48 +1,125 @@
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  /*
> >   * Copyright (C) 2021 Linaro Ltd
> > - *
> >   * Author: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
> >   *
> > + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> > + * Author: Yi Sun <yi.y.sun@xxxxxxxxx>
> > + *
> > + * Copyright (C) 2020 Genesys Logic, Inc.
> > + * Authors: Ben Chuang <ben.chuang@xxxxxxxxxxxxxxxxxxx>
> > + *
> > + * Copyright (C) 2020 Linaro Limited
> > + * Author: AKASHI Takahiro <takahiro.akashi@xxxxxxxxxx>
> > + *
> > + * Copyright (C) 2022 Genesys Logic, Inc.
> > + * Authors: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx>
> > + *
> >   * Support for SD UHS-II cards
> >   */
> >  #include <linux/err.h>
> > +#include <linux/pm_runtime.h>
> >
> >  #include <linux/mmc/host.h>
> >  #include <linux/mmc/card.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sd.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> > +#include "card.h"
> >  #include "core.h"
> >  #include "bus.h"
> >  #include "sd.h"
> > +#include "sd_ops.h"
> >  #include "mmc_ops.h"
> >
> > +#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US       (1 * 1000)      /* 1ms */
> > +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS      100             /* 100ms */
> > +
> >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > +int sd_uhs2_reinit(struct mmc_host *host);
>
> This should be static - and please don't use forward declaration,
> unless it's really necessary.
>
> >
> > -static int sd_uhs2_set_ios(struct mmc_host *host)
> > +/*
> > + * Internal function that does the actual ios call to the host driver,
> > + * optionally printing some debug output.
> > + */
> > +static inline int sd_uhs2_set_ios(struct mmc_host *host)
> >  {
> >         struct mmc_ios *ios = &host->ios;
> >
> > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd, ios->timing);
> > +
> >         return host->ops->uhs2_set_ios(host, ios);
>
> I have made some more thinking around the uhs2 host callbacks.
>
> As we are using the ->uhs2_control() callback for all the other uhs2
> operations, it seems silly to have a separate callback for the ios. In
> other words, I suggest that we drop the ->uhs2_set_ios() callback
> altogether and replace the two users of the above function with direct
> calls to the ->uhs2_control() callback. See more below.
>

I will update it in V7 version.

> >  }
> >
> >  static int sd_uhs2_power_up(struct mmc_host *host)
> >  {
> > +       int err;
> > +
> > +       if (host->ios.power_mode == MMC_POWER_ON)
> > +               return 0;
> > +
> >         host->ios.vdd = fls(host->ocr_avail) - 1;
> >         host->ios.clock = host->f_init;
> >         host->ios.timing = MMC_TIMING_SD_UHS2;
> > -       host->ios.power_mode = MMC_POWER_UP;
> > +       host->ios.power_mode = MMC_POWER_ON;
> >
> > -       return sd_uhs2_set_ios(host);
> > +       err = sd_uhs2_set_ios(host);
>
> As stated above, I suggest we replace the above call with:
> err = host->ops->uhs2_control(host, UHS2_SET_IOS);
>

I will update it in V7 version.

> > +
> > +       mmc_delay(host->uhs2_ios.power_delay_ms);
> > +
> > +       return err;
> >  }
> >
> > -static void sd_uhs2_power_off(struct mmc_host *host)
> > +static int sd_uhs2_power_off(struct mmc_host *host)
> >  {
> > +       if (host->ios.power_mode == MMC_POWER_OFF)
> > +               return 0;
> > +
> >         host->ios.vdd = 0;
> >         host->ios.clock = 0;
> >         host->ios.timing = MMC_TIMING_LEGACY;
> >         host->ios.power_mode = MMC_POWER_OFF;
> >
> > -       sd_uhs2_set_ios(host);
> > +       return sd_uhs2_set_ios(host);
>
> Similar to the above, I suggest we replace the above call with:
> return host->ops->uhs2_control(host, UHS2_SET_IOS);
>

I will update it in V7 version.

> >
> >  /*
> > @@ -61,6 +146,88 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> >   */
> >  static int sd_uhs2_dev_init(struct mmc_host *host)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u32 cnt;
> > +       u32 dap, gap, resp_gap;
> > +       u16 header, arg;
> > +       __be32 payload[UHS2_DEV_INIT_PAYLOAD_LEN];
> > +       u8 gd = 0;
> > +       u8 resp[UHS2_DEV_ENUM_RESP_LEN] = {0};
> > +       int err;
> > +
> > +       dap = host->uhs2_caps.dap;
> > +       gap = host->uhs2_caps.gap;
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 Figure 6-21 to see DEVICE_INIT CCMD format.
> > +        * Head:
> > +        *      - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
> > +        *      - IOADR = CMD_BASE + 002h
> > +        * Payload:
> > +        *      - bit [3:0]  : GAP(Group Allocated Power)
> > +        *      - bit [7:4]  : GD(Group Descriptor)
> > +        *      - bit [11]   : Complete Flag
> > +        *      - bit [15:12]: DAP(Device Allocated Power)
> > +        */
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_4B |
> > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > +        * 1 second.
> > +        */
> > +       cmd.busy_timeout = 1000;
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > +        * When the number of the DEVICE_INIT commands is reach to
> > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > +        * and regard it as an error.
> > +        */
> > +       for (cnt = 0; cnt < 30; cnt++) {
> > +               payload[0] = ((dap & 0xF) << 12) |
> > +                             UHS2_DEV_INIT_COMPLETE_FLAG |
> > +                             ((gd & 0xF) << 4) |
> > +                             (gap & 0xF);
> > +
> > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,
> > +                                    payload, UHS2_DEV_INIT_PAYLOAD_LEN,
> > +                                    resp, UHS2_DEV_INIT_RESP_LEN);
> > +
> > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > +
>
> Nitpick: Unnecessary line break.
>

I will update it in V7 version.

> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                              mmc_hostname(host), __func__, err);
> > +                       return err;
> > +               }
> > +
> > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > +                              mmc_hostname(host));
> > +                       return -EIO;
> > +               }
> > +
> > +               if (resp[5] & 0x8) {
> > +                       host->uhs2_caps.group_desc = gd;
> > +                       return 0;
> > +               }
> > +               resp_gap = resp[4] & 0x0F;
> > +               if (gap == resp_gap)
> > +                       gd++;
> > +       }
> > +       if (cnt == 30) {
>
> There is no need to check cnt here. It's always 30 if we reach this point.
>

I will drop it and update in V7 version.

> > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > +                      mmc_hostname(host));
> > +               return -EIO;
> > +       }
> > +
> >         return 0;
> >  }
>
> [...]
>
> >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header, arg;
> > +       __be32 payload[UHS2_CFG_WRITE_PAYLOAD_LEN];
> > +       u8 nMinDataGap;
> > +       int err;
> > +       u8 resp[5] = {0};
> > +
> > +       /*
> > +        * Use Control Write CCMD to set Generic Setting in Configuration Register.
> > +        * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
> > +        * - IOADR = Generic Setting Register(CFG_BASE + 008h)
> > +        * - Payload = New contents to be written to Generic Setting Register
> > +        */
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_8B |
> > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +
> > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > +               /* Support HD */
> > +               host->uhs2_ios.is_2L_HD_mode = true;
> > +               nMinDataGap = 1;
> > +       } else {
> > +               /* Only support 2L-FD so far */
> > +               host->uhs2_ios.is_2L_HD_mode = false;
> > +               nMinDataGap = 3;
> > +       }
> > +
> > +       /*
> > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > +        * defined in UHS-II addendem Ver1.01 are optional.
> > +        */
> > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > +
> > +       payload[0] = card->uhs2_config.n_lanes_set << UHS2_DEV_CONFIG_N_LANES_POS;
> > +       payload[1] = 0;
> > +       payload[0] = cpu_to_be32(payload[0]);
> > +       payload[1] = cpu_to_be32(payload[1]);
> > +
> > +       /*
> > +        * There is no payload because per spec, there should be
> > +        * no payload field for read CCMD.
> > +        * Plen is set in arg. Per spec, plen for read CCMD
> > +        * represents the len of read data which is assigned in payload
> > +        * of following RES (p136).
> > +        */
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
> > +                            NULL, 0);
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Use Control Write CCMD to set PHY Setting in Configuration Register.
> > +        * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
> > +        * - IOADR = PHY Setting Register(CFG_BASE + 00Ah)
> > +        * - Payload = New contents to be written to PHY Setting Register
> > +        */
> > +       arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_8B |
> > +              (UHS2_DEV_CONFIG_PHY_SET >> 8);
> > +
> > +       if (host->uhs2_caps.speed_range == UHS2_DEV_CONFIG_PHY_SET_SPEED_B) {
> > +               card->uhs2_state |= MMC_UHS2_SPEED_B;
>
> As I suggested earlier, let's set a corresponding bit in ios->timing instead.
>
> > +               card->uhs2_config.speed_range_set =
> > +                                       UHS2_DEV_CONFIG_PHY_SET_SPEED_B;
> > +       } else {
> > +               card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_A;
> > +               card->uhs2_state &= ~MMC_UHS2_SPEED_B;
>
> As I suggested earlier, I think it should be sufficient to clear this
> in sd_uhs2_power_off() (by resetting ios->timing).
>
> > +       }
> > +
> > +       payload[0] = card->uhs2_config.speed_range_set << UHS2_DEV_CONFIG_PHY_SET_SPEED_POS;
> > +
> > +       card->uhs2_config.n_lss_sync_set = (max(card->uhs2_config.n_lss_sync,
> > +                                               host->uhs2_caps.n_lss_sync) >> 2) &
> > +                                          UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
> > +       host->uhs2_caps.n_lss_sync_set = card->uhs2_config.n_lss_sync_set;
> > +
> > +       card->uhs2_config.n_lss_dir_set = (max(card->uhs2_config.n_lss_dir,
> > +                                              host->uhs2_caps.n_lss_dir) >> 3) &
> > +                                         UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
> > +       host->uhs2_caps.n_lss_dir_set = card->uhs2_config.n_lss_dir_set;
> > +
> > +       payload[1] = (card->uhs2_config.n_lss_dir_set << UHS2_DEV_CONFIG_N_LSS_DIR_POS) |
> > +                    card->uhs2_config.n_lss_sync_set;
> > +       payload[0] = cpu_to_be32(payload[0]);
> > +       payload[1] = cpu_to_be32(payload[1]);
> > +
> > +       memset(resp, 0, sizeof(resp));
> > +
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
> > +                            resp, UHS2_CFG_WRITE_PHY_SET_RESP_LEN);
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return err;
> > +       }
> > +
> > +       if ((resp[2] & 0x80)) {
> > +               pr_err("%s: %s: UHS2 CMD not accepted, resp= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, resp[2]);
> > +               return -EIO;
> > +       }
> > +
> > +       /*
> > +        * Use Control Write CCMD to set LINK/TRAN Setting in Configuration Register.
> > +        * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
> > +        * - IOADR = LINK/TRAN Setting Register(CFG_BASE + 00Ch)
> > +        * - Payload = New contents to be written to LINK/TRAN Setting Register
> > +        */
> > +       arg = ((UHS2_DEV_CONFIG_LINK_TRAN_SET & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_WRITE |
> > +               UHS2_NATIVE_CMD_PLEN_8B |
> > +               (UHS2_DEV_CONFIG_LINK_TRAN_SET >> 8);
> > +
> > +       if (card->uhs2_config.app_type == UHS2_DEV_CONFIG_APP_SD_MEM)
> > +               card->uhs2_config.maxblk_len_set = UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN;
> > +       else
> > +               card->uhs2_config.maxblk_len_set = min(card->uhs2_config.maxblk_len,
> > +                                                      host->uhs2_caps.maxblk_len);
> > +       host->uhs2_caps.maxblk_len_set = card->uhs2_config.maxblk_len_set;
> > +
> > +       card->uhs2_config.n_fcu_set = min(card->uhs2_config.n_fcu, host->uhs2_caps.n_fcu);
> > +       host->uhs2_caps.n_fcu_set = card->uhs2_config.n_fcu_set;
> > +
> > +       card->uhs2_config.n_data_gap_set = max(nMinDataGap, card->uhs2_config.n_data_gap);
> > +       host->uhs2_caps.n_data_gap_set = card->uhs2_config.n_data_gap_set;
> > +
> > +       host->uhs2_caps.max_retry_set = 3;
> > +       card->uhs2_config.max_retry_set = host->uhs2_caps.max_retry_set;
> > +
> > +       payload[0] = (card->uhs2_config.maxblk_len_set << UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) |
> > +                    (card->uhs2_config.max_retry_set << UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) |
> > +                    (card->uhs2_config.n_fcu_set << UHS2_DEV_CONFIG_N_FCU_POS);
> > +       payload[1] = card->uhs2_config.n_data_gap_set;
> > +       payload[0] = cpu_to_be32(payload[0]);
> > +       payload[1] = cpu_to_be32(payload[1]);
> > +
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
> > +                            NULL, 0);
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Use Control Write CCMD to set Config Completion(payload bit 63) in Generic Setting
> > +        * Register.
> > +        * Header:
> > +        *      - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
> > +        *      - IOADR = PGeneric Setting Register(CFG_BASE + 008h)
> > +        * Payload:
> > +        *      - bit [63]: Config Completion
> > +        *
> > +        * DLSM transits to Active state immediately when Config Completion is set to 1.
> > +        */
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_8B |
> > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +
> > +       payload[0] = 0;
> > +       payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE;
> > +       payload[0] = cpu_to_be32(payload[0]);
> > +       payload[1] = cpu_to_be32(payload[1]);
> > +
> > +       memset(resp, 0, sizeof(resp));
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
> > +                            resp, UHS2_CFG_WRITE_GENERIC_SET_RESP_LEN);
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return err;
> > +       }
> > +
> > +       /* Set host Config Setting registers */
> > +       err = host->ops->uhs2_control(host, UHS2_SET_CONFIG);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 SET_CONFIG fail!\n", mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +
> > +       return 0;
> > +}
>
> [...]
>
> > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > +{
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header, arg;
> > +       int err;
> > +       struct sd_uhs2_wait_active_state_data cb_data = {
> > +               .host = host,
> > +               .cmd = &cmd
> > +       };
> > +
> > +       /* Change Speed Range at controller side. */
> > +       err = host->ops->uhs2_control(host, UHS2_SET_SPEED_B);
>
> As I asked for in patch5, is this call really needed for sdhci?
>
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n", mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +
> > +       err = sd_uhs2_go_dormant(host, node_id);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Use Control Read CCMD to check Config Completion(bit 63) in Generic Setting Register.
> > +        * - Control Read(R/W=0) with 8-Byte payload(PLEN=10b).
> > +        * - IOADR = Generic Setting Register(CFG_BASE + 008h)
> > +        *
> > +        * When UHS-II card been switched to new speed mode, it will set Config Completion to 1.
> > +        */
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_READ |
> > +               UHS2_NATIVE_CMD_PLEN_8B |
> > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > +       err = __mmc_poll_for_busy(host, UHS2_WAIT_CFG_COMPLETE_PERIOD_US,
> > +                                 UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS,
> > +                                 &__sd_uhs2_wait_active_state_cb, &cb_data);
> > +       if (err) {
> > +               pr_err("%s: %s: Not switch to Active in 100 ms\n", mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > +{
> > +       /*
> > +        * Some systems don't feature a write-protect pin and don't need one.
> > +        * E.g. because they only have micro-SD card slot. For those systems
> > +        * assume that the SD card is always read-write.
> > +        */
> > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > +               return 0;
> > +
> > +       if (!host->ops->get_ro)
> > +               return -1;
> > +
> > +       return host->ops->get_ro(host);
>
> Let's avoid the open coding and use mmc_sd_get_ro() instead.
>
> > +}
> > +
> > +/*
> > + * Mask off any voltages we don't support and select
> > + * the lowest voltage
> > + */
> > +u32 sd_uhs2_select_voltage(struct mmc_host *host, u32 ocr)
> > +{
> > +       int bit;
> > +       int err;
> > +
> > +       /*
> > +        * Sanity check the voltages that the card claims to
> > +        * support.
> > +        */
> > +       if (ocr & 0x7F) {
> > +               dev_warn(mmc_dev(host), "card claims to support voltages below defined range\n");
> > +               ocr &= ~0x7F;
> > +       }
> > +
> > +       ocr &= host->ocr_avail;
> > +       if (!ocr) {
> > +               dev_warn(mmc_dev(host), "no support for card's volts\n");
> > +               return 0;
> > +       }
> > +
> > +       if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> > +               bit = ffs(ocr) - 1;
> > +               ocr &= 3 << bit;
> > +               /* Power cycle */
> > +               err = sd_uhs2_power_off(host);
> > +               if (err)
> > +                       return 0;
> > +               err = sd_uhs2_reinit(host);
>
> As also pointed out by Adrian, this path becomes circular and doesn't
> work as expected.
>
> At this point, I would prefer to keep things as simple as possible, so
> I suggest that we drop this path entirely for UHS2.
>

Can we use sd_uhs2_power and sd_uhs2_power_up instead of the
sd_uhs2_reinit after I reference the mmc_power_cycle function?

> > +               if (err)
> > +                       return 0;
> > +       } else {
> > +               bit = fls(ocr) - 1;
> > +               ocr &= 3 << bit;
>
> Note that, in mmc_select_voltage() the corresponding code has been
> updated in commit 39a72dbfe188 ("mmc: core: properly select voltage
> range without power cycle") that fixes a real problem for us.
>
> By taking the above observations into consideration, it looks like it
> may be better to reuse mmc_select_voltage() for UHS-II after all. To
> prevent the power cycle path for UHS-II, we can just add a check for
> the ios->timings in there. That should work, I think.
>
> > +               if (bit != host->ios.vdd)
> > +                       dev_warn(mmc_dev(host), "exceeding card's volts\n");
> > +       }
> > +
> > +       return ocr;
> > +}
> > +
> >  /*
> >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > @@ -107,14 +893,149 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >   */
> >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
>
> I decided to postpone the review of the following parts below, which
> consists of the legacy card initialization (SD-tran) and the
> power-management support - as it looks like there are enough for you
> to work on for a while.
>
> Let me also ask about the power management support - did you test the
> system suspend/resume path? Just to understand, if this is more an
> attempt to make it work or whether it's actually tested/works?
>
> [...]
>
> Kind regards
> Uffe

Thanks, Victor Shih



[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