Re: [PATCH 6/7] mmc: Implement content of UHS-II card initialization functions

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

 



On Wed, Dec 15, 2021 at 4:29 AM Ulf Hansson <ulf.hansson@xxxxxxxxxx> wrote:
>
>> On Fri, 3 Dec 2021 at 11:51, Jason Lai <jasonlai.genesyslogic@xxxxxxxxx> wrote:
> >
> > From: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx>
> >
> > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > Part 1 - PHY Initialization:
> >          Every host controller may need their own avtivation operation
> >          to establish LINK between controller and card. So we add a new
> >          member function(uhs2_detect_init) in struct mmc_host_ops for host
> >          controller use.
> > Part 2 - Card Initialization:
> >          This part can be divided into 6 substeps.
> >          1. Send UHS-II CCMD DEVICE_INIT to card.
> >          2. Send UHS-II CCMD ENUMERATE to card.
> >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> >             of card.
> >          4. Host compares capabilities of host controller and  card,
> >             then write the negotiated values to Setting field in CFG_REG
> >             of card through UHS-II Native Write CCMD.
> >          5. Switch host controller's clock to Range B if it is supported
> >             by both host controller and card.
> >          6. Execute legacy SD initialization flow.
> > Part 3 - Provide a function to tranaform legacy SD command packet into
> >          UHS-II SD-TRAN DCMD packet.
> >
> > Most of the code added above came from Intel's original patch[1].
> >
> > [1]
> > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > git-send-email-yi.y.sun@xxxxxxxxx/
> >
> > Signed-off-by: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx>
> > ---
> >  drivers/mmc/core/sd_uhs2.c | 831 ++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 810 insertions(+), 21 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index 24aa51a6d..d13283d54 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -3,6 +3,7 @@
> >
> >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> >
> > -static int sd_uhs2_set_ios(struct mmc_host *host)
> > +static void sd_uhs2_set_ios(struct mmc_host *host)
>
> Please don't change the function to return void. It's better to allow
> the callback to return an error code, even if that might not be of
> benefit for your particular host variant.

I have a question about the return type of this function. In
sd_uhs2_set_ios(), we only called
host->ops->set_ios() and the return type of that function was VOID. So
is there a reason
that this function must has a non-void return type?

> >  {
> >         struct mmc_ios *ios = &host->ios;
> >
> > -       return host->ops->uhs2_set_ios(host, ios);
> > +       host->ops->set_ios(host, ios);
> >  }
> >
> > -static int sd_uhs2_power_up(struct mmc_host *host)
> > +static void sd_uhs2_power_up(struct mmc_host *host)
> >  {
>
> Ditto.
>
> >         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;
> >
> > -       return sd_uhs2_set_ios(host);
> > +       sd_uhs2_set_ios(host);
> >  }
> >
> >  static void sd_uhs2_power_off(struct mmc_host *host)
> > @@ -45,6 +48,50 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> >         sd_uhs2_set_ios(host);
> >  }
> >
> > +/**
> > + * sd_uhs2_cmd_assemble() - build up UHS-II command packet which is embeded in
> > + *                          mmc_command structure
> > + * @cmd:       MMC command to executed
> > + * @uhs2_cmd:  UHS2 command corresponded to MMC command
> > + * @header:    Header field of UHS-II command cxpacket
> > + * @arg:       Argument field of UHS-II command packet
> > + * @payload:   Payload field of UHS-II command packet
> > + * @plen:      Payload length
> > + * @resp:      Response buffer is allocated by caller and it is used to keep
> > + *              the response of CM-TRAN command. For SD-TRAN command, uhs2_resp
> > + *              should be null and SD-TRAN command response should be stored in
> > + *              resp of mmc_command.
> > + * @resp_len:  Response buffer length
> > + *
> > + * Different from legacy SD command, there defined several types of packets
> > + * in UHS-II, which include CM-TRAN and SD-TRAN. These packets are then
> > + * transformed to differential signals and transmitted/received between
> > + * transceiver and receiver.
> > + *
> > + * The UHS-II packet structure(uhs2_cmd) are added in structure mmc_command
> > + * and be filled according to the inputed parameters provided by caller.
> > + *
> > + * For mmc requests, call this function before issuing command to SD card.
> > + * Host controller specific request function will send packet in uhs2_cmd
> > + * to UHS-II SD card.
> > + *
>
> I think we discussed the above functions description earlier. The
> above description is just way too overwhelming. What the function does
> is that fills out the uhs2_cmd data structure, that's it.
>
> As this is a static function, I wouldn't mind that you simply drop the
> entire function description above. Or try to make the description
> short and clear.

I will try to do that.

> > + */
> > +static void sd_uhs2_cmd_assemble(struct mmc_command *cmd,
> > +                             struct uhs2_command *uhs2_cmd,
> > +                             u16 header, u16 arg,
> > +                             u32 *payload, u8 plen, u8 *resp, u8 resp_len)
> > +{
> > +       uhs2_cmd->header = header;
> > +       uhs2_cmd->arg = arg;
> > +       uhs2_cmd->payload = payload;
> > +       uhs2_cmd->payload_len = plen * sizeof(u32);
> > +       uhs2_cmd->packet_len = uhs2_cmd->payload_len + 4;
> > +
> > +       cmd->uhs2_cmd = uhs2_cmd;
> > +       cmd->uhs2_resp = resp;
> > +       cmd->uhs2_resp_len = resp_len;
> > +}
> > +
> >  /*
> >   * Run the phy initialization sequence, which mainly relies on the UHS-II host
> >   * to check that we reach the expected electrical state, between the host and
> > @@ -52,16 +99,98 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> >   */
> >  static int sd_uhs2_phy_init(struct mmc_host *host)
> >  {
> > -       return 0;
> > +       int err = 0;
> > +
> > +       /*
> > +        * Every host controller can assign its own function which is used
> > +        * to initialize PHY.
> > +        */
> > +       err = host->ops->uhs2_detect_init(host)
>
> It looks like you didn't declare this callback as part of the struct
> mmc_host_ops, so this does not even compile. Please fix that.
>
> I also suggest moving the above comment into the header file along
> with its declaration, as it's usually where we keep this information.
>
> Moreover, to make it clear what the callback is intended for, I
> suggest renaming it to "uhs2_phy_init".

There are 5 callback functions for UHS-II:
    int   uhs2_detect_init(struct mmc_host *host);
    int   uhs2_set_reg(struct mmc_host *host, enum uhs2_action act);
    void uhs2_disable_clk(struct mmc_host *host);
    void uhs2_enable_clk(struct mmc_host *host);
    void uhs2_post_attach_sd(struct mmc_host *host);
According to your suggestion, I plan to integrate them into a single function,
named "uhs2_do_action", since they all realize the specified function by
setting the controller.

> > +       if (err) {
> > +               pr_err("%s: fail to detect UHS2!\n", mmc_hostname(host));
>
> Maybe "failed to initial phy for UHS-II", is better?

Okay. The string will be modified in the next version.

> > +       }
> > +
> > +       return err;
> >  }
> >
> >  /*
> > - * Do the early initialization of the card, by sending the device init
> > -roadcast
> > + * Do the early initialization of the card, by sending the device init broadcast
>
> Looks like you are correcting a spelling mistake, which is good.
> However, please make this a part of the patch that introduced this,
> earlier in the series.

Do you mean to create a separate patch which only contains typo correction?
If yes, should I collect such changes in previously submitted files
and put them to
a separate patch file  in the next version, even though they are the
same between
previous and next version? Or just apply this rule to later versions?

> >  /*
> >   * Read the UHS-II configuration registers (CFG_REG) of the card, by sending it
> >   * commands and by parsing the responses. Store a copy of the relevant data in
> > - * card->uhs2_config.
> > + * host->uhs2_card_prop.
>
> This isn't correct. The code below is storing the configuration in
> card->uhs2_config, which is the correct thing to do.

Okay. I will rephrase the description in the next version.

> >
> > -/*
> > +/**
> >   * Based on the card's and host's UHS-II capabilities, let's update the
> >   * configuration of the card and the host. This may also include to move to a
> >   * greater speed range/mode. Depending on the updated configuration, we may
> > -eed
> > - * to do a soft reset of the card via sending it a GO_DORMANT_STATE command.
> > + * need to do a soft reset of the card via sending it a GO_DORMANT_STATE
> > + * command.
> >   *
> >   * In the final step, let's check if the card signals "config completion",
> > -hich
> > - * indicates that the card has moved from config state into active state.
> > + * which indicates that the card has moved from config state into active state.
>
> As I said earlier, please fix spelling mistakes as part of the
> patch(es) that introduced them.
>
> >   */
> >  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 = 0, arg = 0;
> > +       u32 payload[2];
> > +       u8 nMinDataGap;
> > +       u8 plen;
> > +       u8 try;
> > +       int err;
> > +       u8 resp[5] = {0};
> > +       u8 resp_len = 5;
> > +
> > +       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->flags |= MMC_UHS2_2L_HD;
> > +               nMinDataGap = 1;
> > +       } else {
> > +               /* Only support 2L-FD so far */
> > +               host->flags &= ~MMC_UHS2_2L_HD;
> > +               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;
> > +
> > +       plen = 2;
> > +       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, plen,
> > +                         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 -EIO;
> > +       }
> > +
> > +       arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_8B |
> > +              (UHS2_DEV_CONFIG_PHY_SET >> 8);
> > +
> > +       for (try = 0; try < 2; try++) {
>
> This loop looks a bit odd. Perhaps you can explain why we need this
> with a comment in the code?
>
> Optionally, we could potentially also try to move some part of it into
> a separate helper function and then just call that from here instead?
> In this way, we may be able to avoid the loop altogether and thus also
> the code should become more readable. What do you think?

Agree. I will try to do it in the next version.

> > +               if (!(resp[2] & 0x80))
> > +                       break;
> > +       }
> > +
> > +       /* Set host Config Setting registers */
> > +       if (!host->ops->uhs2_set_reg ||
>
> Again, I couldn't find this callback being declared, so I guess this
> doesn't compile. Please fix that.
>
> I agree that the callback should not be optional. However, I think
> this is far too late in the initialization process to check it. This
> can be checked already when adding the host in mmc_add_host(), in case
> the host supports UHS-II, for example.
>
> Moreover, I am questioning the name of the callback. It seems like we
> are going to use it for various UHS-II operations. The name
> "uhs2_set_reg" doesn't really explain this. Maybe "uhs2_do_action", or
> if you can come up with something even better, that would be great.

Agree. As mentioned above, I plan to use uhs2_do_action to  replace 5 UHS-II
callback functions. And I will check (host->ops->uhs2_do_action) at
the beginning
of the initialization process.

> > +           host->ops->uhs2_set_reg(host, SET_CONFIG)) {
> > +               pr_err("%s: %s: UHS2 SET_CONFIG fail!\n",
> > +                       mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > +{
> > +
> > +       if (host->ops->uhs2_disable_clk)
>
> Again, another callback that lacks declaration.
>
> > +               host->ops->uhs2_disable_clk(host);
>
> In fact, rather than adding another callback, we could make use of the
> "->uhs2_set_reg" callback?
>
> Or, another option would be to use the ->uhs2_set_ios(). That would be
> similar to what we do for legacy SD cards with the ->set_ios()
> callback.
>
> For example, we can do like this:
> host->ios.power_mode = MMC_POWER_ON;
> host->ios.clock = 0;
> host->ops->uhs2_set_ios(host, &host->ios);
>
> What do you think? Maybe we need a separate host->ios_uhs2 for this too?

As I mentioned above, I intend to use integrated "uhs2_do_action()".
What do you think?

> > +
> > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > +{
> > +
> > +       /* restore sd clock */
> > +       mmc_delay(5);
> > +       if (host->ops->uhs2_enable_clk)
> > +               host->ops->uhs2_enable_clk(host);
>
> Ditto.
>
> > +
> > +       /*
> > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > +        * switch to Active State
> > +        */
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD |
> > +                host->card->uhs2_config.node_id;
>
> Didn't you pass node_id as an in-parameter? Why can't you use that?

Oops! I missed it accidentally in the previous version. I will update
it in the next version.

> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_READ |
> > +               UHS2_NATIVE_CMD_PLEN_8B |
> > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> >
> >
> > @@ -108,8 +724,70 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >   * be set through a legacy CMD6. Note that, the power limit that becomes set,
> >   * survives a soft reset through the GO_DORMANT_STATE command.
> >   */
> > -static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > +static int sd_uhs2_legacy_init(struct mmc_host *host)
>
> Please make this change from the patch that introduced this code.

Okay.

> >  {
> > +       int err;
> > +       u32 ocr, rocr;
> > +
> > +       WARN_ON(!host->claimed);
> > +
> > +       /* Send CMD0 to reset SD card */
> > +       mmc_go_idle(host);
> > +
> > +       /* Send CMD8 to communicate SD interface operation condition */
> > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > +       if (err) {
> > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Send host capacity support information and activate card's
> > +        * initialization process.
> > +        */
> > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +
> > +       if (host->ocr_avail_sd)
> > +               host->ocr_avail = host->ocr_avail_sd;
>
> Please drop this part. I don't want to encourage host's to use
> ocr_avail_sd (for reasons that we can discuss another time).

Okay.

> If we need something specific for UHS-II, then it's better to add that.
>
> > +
> > +       /*
> > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > +        * these bits as being in-valid and especially also bit7.
> > +        */
> > +       ocr &= ~0x7FFF;
> > +       rocr = mmc_select_voltage(host, ocr);
> > +
> > +       /*
> > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > +        * ocr value to rocr.
> > +        */
> > +       if (!rocr) {
> > +               if (host->ocr_avail) {
> > +                       rocr = host->ocr_avail;
> > +               } else {
> > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       /*
> > +        * Get CID, send relative address, get CSD are the same as legacy
> > +        * sd, call mmc_sd_init_card() in sd.c directly
> > +        */
> > +       err = mmc_sd_init_card(host, rocr, NULL);
> > +       if (err) {
> > +               pr_err("%s: %s: mmc_sd_init_card() fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return err;
> > +       }
>
> So this means we will start using the host callbacks for the legacy SD
> interface, like ->set_ios() for example.

I will remove this callback and reorganize codes in this portion.

> In other words, data in host->ios.* seems to be used, both for UHS-II
> and legacy SD. That can be messy for host drivers to manage, I think.
> Maybe we should add a separate host->ios_uhs2.*, that we use solely
> for UHS2 instead?

This topic can be discussed.
The ios structure can continue to be used after adding new definitions to
certain fields, for example, adding MMC_TIMING_UHS2 for ios.timing.

> There is also another problem here. mmc_sd_init_card() will allocate a
> new struct mmc_card and assign it to host->card, if it succeeds with
> initialization.
>
> By looking at the changes below, it looks like you will assign
> host->card, with the struct mmc_card that we have allocated for UHS-II
> in sd_uhs2_init_card(), prior to calling sd_uhs2_legacy_init(). Not
> only will this lead to a memory leak, but more importantly we will
> lose all the information that we have parsed/stored from the card for
> UHS-II, right?
>
> Don't we need to keep the UHS-II information of the card around? Or is
> it okay to throw it away after initialization?
>
> What am I missing?

You are right. I will reorganize this portion.

> > +
> >         return 0;
> >  }
> >
> > @@ -124,12 +802,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         int err;
> >
> >         err = sd_uhs2_dev_init(host);
> > -       if (err)
> > +       if (err) {
> > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> >                 return err;
> > +       }
> >
> >         err = sd_uhs2_enum(host, &node_id);
> > -       if (err)
> > +       if (err) {
> > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> >                 return err;
> > +       }
> >
> >         card = mmc_alloc_card(host, &sd_type);
> >         if (IS_ERR(card))
> > @@ -142,15 +824,26 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > +       /* Change to Speed Range B if it is supported */
> > +       if (host->flags & MMC_UHS2_SPEED_B) {
>
> I noticed that you have added the "host->flags", to keep track of
> UHS-II states and some UHS-II capabilities of the host. I would rather
> see that UHS-II host capabilities should be part of the
> host->uhs2_caps.*.

Agree. I will move the "flags" field to struct sd_uhs2_caps{} in the
next version.

> When it comes to keeping track of UHS-II states, like when we want to
> move to a new speed mode for example, that may be better to keep as a
> part of a new host->ios_uhs2.*, as I kind of indicated above.

UHS-II speed mode is only set at initialization phase and it is not
changed before hardware reset.
Hence, I think it's not necessary to place sd_uhs2_change_speed() into
host->ios_uhs2.*.
Maybe we can discuss host->ios_uhs2.* after you check my next submission.

> > +               err = sd_uhs2_change_speed(host, node_id);
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > +                               mmc_hostname(host), __func__);
> > +                       return err;
> > +               }
> > +       }
> > +
> >         err = sd_uhs2_config_write(host, card);
> >         if (err)
> >                 goto err;
> >
> > -       err = sd_uhs2_legacy_init(host, card);
> > +       host->card = card;
> > +
> > +       err = sd_uhs2_legacy_init(host);
> >         if (err)
> >                 goto err;
> >
> > -       host->card = card;
>
> This is the part that will lead to the memory leak of a struct mmc_card.

I will reorganize this portion to fix this memory leak.

> >         return 0;
> >
> >  err:
> > @@ -217,6 +910,78 @@ static int sd_uhs2_hw_reset(struct mmc_host *host)
> >         return 0;
> >  }
>
> [...]
>
> I have stopped the review at this point, let's see if we can conclude
> on the way forward around my comments on the parts above, to start
> with.
>
> Kind regards
> Uffe

I will start preparing for the next version submission after receiving
your response to my question.
Thank you for your patience.

kind regards
Jason Lai



[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