RE: [PATCH v5] mmc: support BKOPS feature for eMMC

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

 




> -----Original Message-----
> From: linux-mmc-owner@xxxxxxxxxxxxxxx
> [mailto:linux-mmc-owner@xxxxxxxxxxxxxxx] On Behalf Of S, Venkatraman
> Sent: Saturday, January 07, 2012 12:42 AM
> To: Jaehoon Chung
> Cc: linux-mmc; Chris Ball; Kyungmin Park; Hanumath Prasad;
> per.forlin@xxxxxxxxxxxxxx; Sebastian Rasmussen; Dong, Chuanxiao; Konstantin
> Dorfman
> Subject: Re: [PATCH v5] mmc: support BKOPS feature for eMMC
> 
> On Thu, Jan 5, 2012 at 6:25 AM, Jaehoon Chung <jh80.chung@xxxxxxxxxxx>
> wrote:
> > Enable eMMC background operations (BKOPS) feature.
> >
> > If URGENT_BKOPS is set after a response, note that BKOPS
> > are required. After all I/O requests are finished, run
> > BKOPS if required. Should read/write operations be requested
> > during BKOPS, first issue HPI to interrupt the ongoing BKOPS
> > and then service the request.
> >
> > If you want to enable this feature, set MMC_CAP2_BKOPS.
> >
> > Future considerations
> >  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
> >  * Interrupt ongoing BKOPS before powering off the card.
> >  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
> >
> > Signed-off-by: Jaehoon Chung <jh80.chung@xxxxxxxxxxx>
> > Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
> > ---
> > Changelog V5:
> >        - Rebase based on the latest mmc-next.
> >        - modify codes based-on Chris's comment
> > Changelog V4:
> >        - Add mmc_read_bkops_status
> >        - When URGENT_BKOPS(level2-3), didn't use HPI command.
> >        - In mmc_switch(), use R1B/R1 according to level.
> > Changelog V3:
> >        - move the bkops setting's location in mmc_blk_issue_rw_rq
> >        - modify condition checking
> >        - bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
> >        - remove the unused code
> >        - change pr_debug instead of pr_warn in mmc_send_hpi_cmd
> >        - Add the Future consideration suggested by Per
> > Changelog V2:
> >        - Use EXCEPTION_STATUS instead of URGENT_BKOPS
> >        - Add function to check Exception_status(for eMMC4.5)
> >
> >  drivers/mmc/card/block.c   |   11 +++++
> >  drivers/mmc/card/queue.c   |    3 +
> >  drivers/mmc/core/core.c    |  105
> ++++++++++++++++++++++++++++++++++++++++++++
> >  drivers/mmc/core/mmc.c     |    8 +++
> >  drivers/mmc/core/mmc_ops.c |   12 +++++-
> >  include/linux/mmc/card.h   |   13 +++++
> >  include/linux/mmc/core.h   |    4 ++
> >  include/linux/mmc/host.h   |    1 +
> >  include/linux/mmc/mmc.h    |   19 ++++++++
> >  9 files changed, 175 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> > index 0cad48a..5e3c580 100644
> > --- a/drivers/mmc/card/block.c
> > +++ b/drivers/mmc/card/block.c
> > @@ -1273,6 +1273,17 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue
> *mq, struct request *rqc)
> >                type = rq_data_dir(req) == READ ? MMC_BLK_READ :
> MMC_BLK_WRITE;
> >                mmc_queue_bounce_post(mq_rq);
> >
> > +               /*
> > +                * Check BKOPS urgency from each R1 response
> > +                */
> > +               if (mmc_card_mmc(card) &&
> > +                       (brq->cmd.flags == MMC_RSP_R1B ||
> > +                        brq->cmd.flags == MMC_RSP_R1) &&
> > +                       (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) {
> > +                       if (mmc_is_exception_event(card,
> EXT_CSD_URGENT_BKOPS))
> > +                               mmc_card_set_need_bkops(card);
> > +               }
> > +
> >                switch (status) {
> >                case MMC_BLK_SUCCESS:
> >                case MMC_BLK_PARTIAL:
> > diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> > index 2517547..fd11a31 100644
> > --- a/drivers/mmc/card/queue.c
> > +++ b/drivers/mmc/card/queue.c
> > @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
> >                spin_unlock_irq(q->queue_lock);
> >
> >                if (req || mq->mqrq_prev->req) {
> > +                       if (mmc_card_doing_bkops(mq->card))
> > +                               mmc_interrupt_bkops(mq->card);
> >                        set_current_state(TASK_RUNNING);
> >                        mq->issue_fn(mq, req);
> >                } else {
> > @@ -73,6 +75,7 @@ static int mmc_queue_thread(void *d)
> >                                set_current_state(TASK_RUNNING);
> >                                break;
> >                        }
> > +                       mmc_start_bkops(mq->card);
> >                        up(&mq->thread_sem);
> >                        schedule();
> >                        down(&mq->thread_sem);
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 4770807..4579b24 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -238,6 +238,50 @@ mmc_start_request(struct mmc_host *host, struct
> mmc_request *mrq)
> >        host->ops->request(host, mrq);
> >  }
> >
> > +/**
> > + *     mmc_start_bkops - start BKOPS for supported cards
> > + *     @card: MMC card to start BKOPS
> > + *
> > + *     Start background operations whenever requested.
> > + *     when the urgent BKOPS bit is set in a R1 command response
> > + *     then background operations should be started immediately.
> > +*/
> > +void mmc_start_bkops(struct mmc_card *card)
> > +{
> > +       int err;
> > +       unsigned long flags;
> > +
> > +       BUG_ON(!card);
> > +       if (!card->ext_csd.bkops_en || !(card->host->caps2 &
> MMC_CAP2_BKOPS))
> > +               return;
> > +
> > +       /*
> > +        * If card is already doing bkops or need for
> > +        * bkops flag is not set, then do nothing just
> > +        * return
> > +        */
> > +       if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
> > +               return;
> > +
> > +       mmc_claim_host(card->host);
> > +       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> > +                       EXT_CSD_BKOPS_START, 1, 0);
> > +       if (err) {
> > +               pr_warning("%s: error %d starting bkops\n",
> > +                          mmc_hostname(card->host), err);
> > +               mmc_card_clr_need_bkops(card);
> > +               goto out;
> > +       }
> > +       spin_lock_irqsave(&card->host->lock, flags);
> > +       mmc_card_clr_need_bkops(card);
> > +       if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2)
> > +               mmc_card_set_doing_bkops(card);
> > +       spin_unlock_irqrestore(&card->host->lock, flags);
> > +out:
> > +       mmc_release_host(card->host);
> > +}
> > +EXPORT_SYMBOL(mmc_start_bkops);
> > +
> >  static void mmc_wait_done(struct mmc_request *mrq)
> >  {
> >        complete(&mrq->completion);
> > @@ -472,6 +516,67 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
> mmc_command *cmd, int retries
> >  EXPORT_SYMBOL(mmc_wait_for_cmd);
> >
> >  /**
> > + *     mmc_interrupt_bkops - interrupt ongoing BKOPS
> > + *     @card: MMC card to check BKOPS
> > + *
> > + *     Send HPI command to interrupt ongoing background operations,
> > + *     to allow rapid servicing of foreground operations,e.g. read/
> > + *     writes. Wait until the card comes out of the programming state
> > + *     to avoid errors in servicing read/write requests.
> > + */
> > +int mmc_interrupt_bkops(struct mmc_card *card)
> > +{
> > +       int err = 0;
> > +       unsigned long flags;
> > +
> > +       BUG_ON(!card);
> > +
> > +       err = mmc_interrupt_hpi(card);
> > +
> > +       spin_lock_irqsave(&card->host->lock, flags);
> > +       mmc_card_clr_doing_bkops(card);
> > +       spin_unlock_irqrestore(&card->host->lock, flags);
> > +
> > +       return err;
> > +}
> > +EXPORT_SYMBOL(mmc_interrupt_bkops);
> > +
> > +int mmc_read_bkops_status(struct mmc_card *card)
> > +{
> > +       int err;
> > +       u8 ext_csd[512];
> > +
> > +       err = mmc_send_ext_csd(card, ext_csd);
> > +       if (err)
> > +               return err;
> > +
> > +       card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
> > +       card->ext_csd.raw_exception_status =
> ext_csd[EXT_CSD_EXCEPTION_STATUS];
> > +
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(mmc_read_bkops_status);
> > +
> > +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
> > +{
> > +       int err;
> > +
> > +       err = mmc_read_bkops_status(card);
> > +       if (err) {
> > +               pr_err("%s: Didn't read bkops status : %d\n",
> > +                      mmc_hostname(card->host), err);
> > +               return 0;
> > +       }
> > +
> > +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
> > +       if (card->ext_csd.rev == 5)
> > +               return 1;
> > +
> > +       return (card->ext_csd.raw_exception_status & value) ? 1 : 0;
> > +}
> > +EXPORT_SYMBOL(mmc_is_exception_event);
> > +
> > +/**
> >  *     mmc_set_data_timeout - set the timeout for a data command
> >  *     @data: data phase for command
> >  *     @card: the MMC card associated with the data transfer
> > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> > index 4af69d0..e132fa8 100644
> > --- a/drivers/mmc/core/mmc.c
> > +++ b/drivers/mmc/core/mmc.c
> > @@ -459,6 +459,14 @@ static int mmc_read_ext_csd(struct mmc_card *card,
> u8 *ext_csd)
> >        }
> >
> >        if (card->ext_csd.rev >= 5) {
> > +               /* check whether the eMMC card support BKOPS */
> > +               if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
> > +                       card->ext_csd.bkops = 1;
> > +                       card->ext_csd.bkops_en =
> ext_csd[EXT_CSD_BKOPS_EN];
> > +                       card->ext_csd.raw_bkops_status =
> > +                               ext_csd[EXT_CSD_BKOPS_STATUS];
> > +               }
> > +
> 
> In my understanding, the EXT_CSD_BKOPS_EN should be _updated_by_ the
> host controller to 1 at initialization time, to indicate to the
> device, as per the spec.
> Otherwise, the device would decide that the host doesn't support
> software controlled BKOPS.
EXT_CSD_BKOPS_EN is a onetime programmable register, which is not suitable for driver to touch I think. This can be updated by host controller, but should not happened in driver. This register can be configured by some platform firmware or some eMMC FW from vendor. For what driver needs to do, is just to read this onetime programmable and to know whether BKOPS is enabled. It is like hardware reset feature. RST_n_FUNCTION is only a onetime programmable register which can indicate whether hardware reset feature is enabled. Driver just needs to read it out and to know whether hardware reset feature can be used. So I agree with Jaehoon Chung's implementation.

BR
Chuanxiao
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux