[V3] mmc: mmci: Clarify comments and some code for busy detection

Message ID 20190723122809.17509-1-ulf.hansson@linaro.org
State New
Headers show
Series
  • [V3] mmc: mmci: Clarify comments and some code for busy detection
Related show

Commit Message

Ulf Hansson July 23, 2019, 12:28 p.m.
The code dealing with busy detection is somewhat complicated. In a way to
make it a bit clearer, let's try to clarify the comments in the code about
it.

Additionally, move the part for clearing the so called busy start IRQ, to
the place where the IRQ is actually delivered. Ideally, this should make
the code a bit more robust.

Finally, to improve understanding of the code and the sequence of the busy
detection, move the corresponding code around a bit in mmci_cmd_irq().

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

---

Changes in v3:
	- Move some code arround to make it even more clear how the busy
	detection works. Updated the changelog accordingly.

---
 drivers/mmc/host/mmci.c | 69 ++++++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 32 deletions(-)

-- 
2.17.1

Comments

Jean Nicolas GRAUX July 23, 2019, 1 p.m. | #1
Hi Ulf,

I did a quick test booting on emmc with that new patch.
Looks good to me.

Best regards,
Jean-Nicolas.

On 7/23/19 2:28 PM, Ulf Hansson wrote:
> The code dealing with busy detection is somewhat complicated. In a way to

> make it a bit clearer, let's try to clarify the comments in the code about

> it.

>

> Additionally, move the part for clearing the so called busy start IRQ, to

> the place where the IRQ is actually delivered. Ideally, this should make

> the code a bit more robust.

>

> Finally, to improve understanding of the code and the sequence of the busy

> detection, move the corresponding code around a bit in mmci_cmd_irq().

>

> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

> ---

>

> Changes in v3:

> 	- Move some code arround to make it even more clear how the busy

> 	detection works. Updated the changelog accordingly.

>

> ---

>   drivers/mmc/host/mmci.c | 69 ++++++++++++++++++++++-------------------

>   1 file changed, 37 insertions(+), 32 deletions(-)

>

> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c

> index 356833a606d5..4c35e7609c89 100644

> --- a/drivers/mmc/host/mmci.c

> +++ b/drivers/mmc/host/mmci.c

> @@ -1222,47 +1222,58 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,

>   	      (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))

>   		return;

>   

> -	/*

> -	 * ST Micro variant: handle busy detection.

> -	 */

> +	/* Handle busy detection on DAT0 if the variant supports it. */

>   	if (busy_resp && host->variant->busy_detect) {

>   

> -		/* We are busy with a command, return */

> -		if (host->busy_status &&

> -		    (status & host->variant->busy_detect_flag))

> -			return;

> -

>   		/*

> -		 * We were not busy, but we now got a busy response on

> -		 * something that was not an error, and we double-check

> -		 * that the special busy status bit is still set before

> -		 * proceeding.

> +		 * Before unmasking for the busy end IRQ, confirm that the

> +		 * command was sent successfully. To keep track of having a

> +		 * command in-progress, waiting for busy signaling to end,

> +		 * store the status in host->busy_status.

> +		 *

> +		 * Note that, the card may need a couple of clock cycles before

> +		 * it starts signaling busy on DAT0, hence re-read the

> +		 * MMCISTATUS register here, to allow the busy bit to be set.

> +		 * Potentially we may even need to poll the register for a

> +		 * while, to allow it to be set, but tests indicates that it

> +		 * isn't needed.

>   		 */

>   		if (!host->busy_status &&

>   		    !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&

>   		    (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {

>   

> -			/* Clear the busy start IRQ */

> -			writel(host->variant->busy_detect_mask,

> -			       host->base + MMCICLEAR);

> -

> -			/* Unmask the busy end IRQ */

>   			writel(readl(base + MMCIMASK0) |

>   			       host->variant->busy_detect_mask,

>   			       base + MMCIMASK0);

> -			/*

> -			 * Now cache the last response status code (until

> -			 * the busy bit goes low), and return.

> -			 */

> +

>   			host->busy_status =

>   				status & (MCI_CMDSENT|MCI_CMDRESPEND);

>   			return;

>   		}

>   

>   		/*

> -		 * At this point we are not busy with a command, we have

> -		 * not received a new busy request, clear and mask the busy

> -		 * end IRQ and fall through to process the IRQ.

> +		 * If there is a command in-progress that has been successfully

> +		 * sent, then bail out if busy status is set and wait for the

> +		 * busy end IRQ.

> +		 *

> +		 * Note that, the HW triggers an IRQ on both edges while

> +		 * monitoring DAT0 for busy completion, but there is only one

> +		 * status bit in MMCISTATUS for the busy state. Therefore

> +		 * both the start and the end interrupts needs to be cleared,

> +		 * one after the other. So, clear the busy start IRQ here.

> +		 */

> +		if (host->busy_status &&

> +		    (status & host->variant->busy_detect_flag)) {

> +			writel(host->variant->busy_detect_mask,

> +			       host->base + MMCICLEAR);

> +			return;

> +		}

> +

> +		/*

> +		 * If there is a command in-progress that has been successfully

> +		 * sent and the busy bit isn't set, it means we have received

> +		 * the busy end IRQ. Clear and mask the IRQ, then continue to

> +		 * process the command.

>   		 */

>   		if (host->busy_status) {

>   

> @@ -1508,14 +1519,8 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)

>   		}

>   

>   		/*

> -		 * We intentionally clear the MCI_ST_CARDBUSY IRQ (if it's

> -		 * enabled) in mmci_cmd_irq() function where ST Micro busy

> -		 * detection variant is handled. Considering the HW seems to be

> -		 * triggering the IRQ on both edges while monitoring DAT0 for

> -		 * busy completion and that same status bit is used to monitor

> -		 * start and end of busy detection, special care must be taken

> -		 * to make sure that both start and end interrupts are always

> -		 * cleared one after the other.

> +		 * Busy detection is managed by mmci_cmd_irq(), including to

> +		 * clear the corresponding IRQ.

>   		 */

>   		status &= readl(host->base + MMCIMASK0);

>   		if (host->variant->busy_detect)
Ulf Hansson July 23, 2019, 1:52 p.m. | #2
On Tue, 23 Jul 2019 at 15:00, Jean Nicolas GRAUX
<jean-nicolas.graux@st.com> wrote:
>

> Hi Ulf,

>

> I did a quick test booting on emmc with that new patch.

> Looks good to me.


Thanks, I will take that as a tested/reviewed-by tag and amend the
patch with that when I queue it up.

Kind regards
Uffe

>

> Best regards,

> Jean-Nicolas.

>

> On 7/23/19 2:28 PM, Ulf Hansson wrote:

> > The code dealing with busy detection is somewhat complicated. In a way to

> > make it a bit clearer, let's try to clarify the comments in the code about

> > it.

> >

> > Additionally, move the part for clearing the so called busy start IRQ, to

> > the place where the IRQ is actually delivered. Ideally, this should make

> > the code a bit more robust.

> >

> > Finally, to improve understanding of the code and the sequence of the busy

> > detection, move the corresponding code around a bit in mmci_cmd_irq().

> >

> > Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

> > ---

> >

> > Changes in v3:

> >       - Move some code arround to make it even more clear how the busy

> >       detection works. Updated the changelog accordingly.

> >

> > ---

> >   drivers/mmc/host/mmci.c | 69 ++++++++++++++++++++++-------------------

> >   1 file changed, 37 insertions(+), 32 deletions(-)

> >

> > diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c

> > index 356833a606d5..4c35e7609c89 100644

> > --- a/drivers/mmc/host/mmci.c

> > +++ b/drivers/mmc/host/mmci.c

> > @@ -1222,47 +1222,58 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,

> >             (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))

> >               return;

> >

> > -     /*

> > -      * ST Micro variant: handle busy detection.

> > -      */

> > +     /* Handle busy detection on DAT0 if the variant supports it. */

> >       if (busy_resp && host->variant->busy_detect) {

> >

> > -             /* We are busy with a command, return */

> > -             if (host->busy_status &&

> > -                 (status & host->variant->busy_detect_flag))

> > -                     return;

> > -

> >               /*

> > -              * We were not busy, but we now got a busy response on

> > -              * something that was not an error, and we double-check

> > -              * that the special busy status bit is still set before

> > -              * proceeding.

> > +              * Before unmasking for the busy end IRQ, confirm that the

> > +              * command was sent successfully. To keep track of having a

> > +              * command in-progress, waiting for busy signaling to end,

> > +              * store the status in host->busy_status.

> > +              *

> > +              * Note that, the card may need a couple of clock cycles before

> > +              * it starts signaling busy on DAT0, hence re-read the

> > +              * MMCISTATUS register here, to allow the busy bit to be set.

> > +              * Potentially we may even need to poll the register for a

> > +              * while, to allow it to be set, but tests indicates that it

> > +              * isn't needed.

> >                */

> >               if (!host->busy_status &&

> >                   !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&

> >                   (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {

> >

> > -                     /* Clear the busy start IRQ */

> > -                     writel(host->variant->busy_detect_mask,

> > -                            host->base + MMCICLEAR);

> > -

> > -                     /* Unmask the busy end IRQ */

> >                       writel(readl(base + MMCIMASK0) |

> >                              host->variant->busy_detect_mask,

> >                              base + MMCIMASK0);

> > -                     /*

> > -                      * Now cache the last response status code (until

> > -                      * the busy bit goes low), and return.

> > -                      */

> > +

> >                       host->busy_status =

> >                               status & (MCI_CMDSENT|MCI_CMDRESPEND);

> >                       return;

> >               }

> >

> >               /*

> > -              * At this point we are not busy with a command, we have

> > -              * not received a new busy request, clear and mask the busy

> > -              * end IRQ and fall through to process the IRQ.

> > +              * If there is a command in-progress that has been successfully

> > +              * sent, then bail out if busy status is set and wait for the

> > +              * busy end IRQ.

> > +              *

> > +              * Note that, the HW triggers an IRQ on both edges while

> > +              * monitoring DAT0 for busy completion, but there is only one

> > +              * status bit in MMCISTATUS for the busy state. Therefore

> > +              * both the start and the end interrupts needs to be cleared,

> > +              * one after the other. So, clear the busy start IRQ here.

> > +              */

> > +             if (host->busy_status &&

> > +                 (status & host->variant->busy_detect_flag)) {

> > +                     writel(host->variant->busy_detect_mask,

> > +                            host->base + MMCICLEAR);

> > +                     return;

> > +             }

> > +

> > +             /*

> > +              * If there is a command in-progress that has been successfully

> > +              * sent and the busy bit isn't set, it means we have received

> > +              * the busy end IRQ. Clear and mask the IRQ, then continue to

> > +              * process the command.

> >                */

> >               if (host->busy_status) {

> >

> > @@ -1508,14 +1519,8 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)

> >               }

> >

> >               /*

> > -              * We intentionally clear the MCI_ST_CARDBUSY IRQ (if it's

> > -              * enabled) in mmci_cmd_irq() function where ST Micro busy

> > -              * detection variant is handled. Considering the HW seems to be

> > -              * triggering the IRQ on both edges while monitoring DAT0 for

> > -              * busy completion and that same status bit is used to monitor

> > -              * start and end of busy detection, special care must be taken

> > -              * to make sure that both start and end interrupts are always

> > -              * cleared one after the other.

> > +              * Busy detection is managed by mmci_cmd_irq(), including to

> > +              * clear the corresponding IRQ.

> >                */

> >               status &= readl(host->base + MMCIMASK0);

> >               if (host->variant->busy_detect)

>

Patch

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 356833a606d5..4c35e7609c89 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1222,47 +1222,58 @@  mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 	      (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))
 		return;
 
-	/*
-	 * ST Micro variant: handle busy detection.
-	 */
+	/* Handle busy detection on DAT0 if the variant supports it. */
 	if (busy_resp && host->variant->busy_detect) {
 
-		/* We are busy with a command, return */
-		if (host->busy_status &&
-		    (status & host->variant->busy_detect_flag))
-			return;
-
 		/*
-		 * We were not busy, but we now got a busy response on
-		 * something that was not an error, and we double-check
-		 * that the special busy status bit is still set before
-		 * proceeding.
+		 * Before unmasking for the busy end IRQ, confirm that the
+		 * command was sent successfully. To keep track of having a
+		 * command in-progress, waiting for busy signaling to end,
+		 * store the status in host->busy_status.
+		 *
+		 * Note that, the card may need a couple of clock cycles before
+		 * it starts signaling busy on DAT0, hence re-read the
+		 * MMCISTATUS register here, to allow the busy bit to be set.
+		 * Potentially we may even need to poll the register for a
+		 * while, to allow it to be set, but tests indicates that it
+		 * isn't needed.
 		 */
 		if (!host->busy_status &&
 		    !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
 		    (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
 
-			/* Clear the busy start IRQ */
-			writel(host->variant->busy_detect_mask,
-			       host->base + MMCICLEAR);
-
-			/* Unmask the busy end IRQ */
 			writel(readl(base + MMCIMASK0) |
 			       host->variant->busy_detect_mask,
 			       base + MMCIMASK0);
-			/*
-			 * Now cache the last response status code (until
-			 * the busy bit goes low), and return.
-			 */
+
 			host->busy_status =
 				status & (MCI_CMDSENT|MCI_CMDRESPEND);
 			return;
 		}
 
 		/*
-		 * At this point we are not busy with a command, we have
-		 * not received a new busy request, clear and mask the busy
-		 * end IRQ and fall through to process the IRQ.
+		 * If there is a command in-progress that has been successfully
+		 * sent, then bail out if busy status is set and wait for the
+		 * busy end IRQ.
+		 *
+		 * Note that, the HW triggers an IRQ on both edges while
+		 * monitoring DAT0 for busy completion, but there is only one
+		 * status bit in MMCISTATUS for the busy state. Therefore
+		 * both the start and the end interrupts needs to be cleared,
+		 * one after the other. So, clear the busy start IRQ here.
+		 */
+		if (host->busy_status &&
+		    (status & host->variant->busy_detect_flag)) {
+			writel(host->variant->busy_detect_mask,
+			       host->base + MMCICLEAR);
+			return;
+		}
+
+		/*
+		 * If there is a command in-progress that has been successfully
+		 * sent and the busy bit isn't set, it means we have received
+		 * the busy end IRQ. Clear and mask the IRQ, then continue to
+		 * process the command.
 		 */
 		if (host->busy_status) {
 
@@ -1508,14 +1519,8 @@  static irqreturn_t mmci_irq(int irq, void *dev_id)
 		}
 
 		/*
-		 * We intentionally clear the MCI_ST_CARDBUSY IRQ (if it's
-		 * enabled) in mmci_cmd_irq() function where ST Micro busy
-		 * detection variant is handled. Considering the HW seems to be
-		 * triggering the IRQ on both edges while monitoring DAT0 for
-		 * busy completion and that same status bit is used to monitor
-		 * start and end of busy detection, special care must be taken
-		 * to make sure that both start and end interrupts are always
-		 * cleared one after the other.
+		 * Busy detection is managed by mmci_cmd_irq(), including to
+		 * clear the corresponding IRQ.
 		 */
 		status &= readl(host->base + MMCIMASK0);
 		if (host->variant->busy_detect)