linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] Prevent races when doing read-modify-write of INTMASK
@ 2013-10-15 22:39 Doug Anderson
  2013-10-15 22:39 ` [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts Doug Anderson
  2013-10-15 22:39 ` [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock Doug Anderson
  0 siblings, 2 replies; 15+ messages in thread
From: Doug Anderson @ 2013-10-15 22:39 UTC (permalink / raw)
  To: Chris Ball
  Cc: Jaehoon Chung, Seungwon Jeon, James Hogan, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, Doug Anderson, linux-mmc, linux-kernel

Bing Zhao at Marvell discovered a race in the way that dw_mmc was
doing a read-modify-write of the INTMASK register.  This 2-patch
series attempts to fix the problem using a simple spinlock.  In order
to do so cleanly, we include a patch to tidy up the way that we
disable low power mode when using SDIO interrupts.

This patch series was not tested on ToT Linux other than basic
compiling and booting, since we don't have the whole Marvell SDIO
stack up and running in mainline yet.  This series is based on
mmc-next (e76b855 mmc: sdhci-esdhc-imx: set actual_clock in clock
setting) merged atop mainlinx Linux.


Doug Anderson (2):
  mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock

 drivers/mmc/host/dw_mmc.c  | 79 +++++++++++++++++++++++++++-------------------
 drivers/mmc/host/dw_mmc.h  |  1 +
 include/linux/mmc/dw_mmc.h |  6 ++++
 3 files changed, 54 insertions(+), 32 deletions(-)

-- 
1.8.4


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-15 22:39 [PATCH 0/2] Prevent races when doing read-modify-write of INTMASK Doug Anderson
@ 2013-10-15 22:39 ` Doug Anderson
  2013-10-18  9:42   ` Jaehoon Chung
  2013-10-15 22:39 ` [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock Doug Anderson
  1 sibling, 1 reply; 15+ messages in thread
From: Doug Anderson @ 2013-10-15 22:39 UTC (permalink / raw)
  To: Chris Ball
  Cc: Jaehoon Chung, Seungwon Jeon, James Hogan, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, Doug Anderson, linux-mmc, linux-kernel

In the patch (9623b5b mmc: dw_mmc: Disable low power mode if SDIO
interrupts are used) I added code that disabled the low power mode of
dw_mmc when SDIO interrupts are used.  That code worked but always
felt a little hacky because we ended up disabling low power as a side
effect of the first enable_sdio_irq() call.  That wouldn't be so bad
except that disabling low power involves a complicated process of
writing to the CMD/CMDARG registers and that extra process makes it
difficult to cleanly the read-modify-write race in
dw_mci_enable_sdio_irq() (see future patch in the series).

Change the code to take advantage of the init_card() callback of the
mmc core to know when an SDIO card has been inserted.  If we've got a
SDIO card and we're configured to use SDIO Interrupts then turn off
"low power mode" right away.

Signed-off-by: Doug Anderson <dianders@chromium.org>
---
 drivers/mmc/host/dw_mmc.c | 68 ++++++++++++++++++++++++-----------------------
 drivers/mmc/host/dw_mmc.h |  1 +
 2 files changed, 36 insertions(+), 33 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 0a6a512..1b75816 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -27,6 +27,7 @@
 #include <linux/stat.h>
 #include <linux/delay.h>
 #include <linux/irq.h>
+#include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sdio.h>
@@ -822,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
 
 		/* enable clock; only low power if no SDIO */
 		clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
-		if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id)))
+		if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags))
 			clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
 		mci_writel(host, CLKENA, clk_en_a);
 
@@ -1050,27 +1051,37 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
 	return present;
 }
 
-/*
- * Disable lower power mode.
- *
- * Low power mode will stop the card clock when idle.  According to the
- * description of the CLKENA register we should disable low power mode
- * for SDIO cards if we need SDIO interrupts to work.
- *
- * This function is fast if low power mode is already disabled.
- */
-static void dw_mci_disable_low_power(struct dw_mci_slot *slot)
+static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
 {
+	struct dw_mci_slot *slot = mmc_priv(mmc);
 	struct dw_mci *host = slot->host;
-	u32 clk_en_a;
-	const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
 
-	clk_en_a = mci_readl(host, CLKENA);
+	/*
+	 * Low power mode will stop the card clock when idle.  According to the
+	 * description of the CLKENA register we should disable low power mode
+	 * for SDIO cards if we need SDIO interrupts to work.
+	 */
+	if (mmc->caps | MMC_CAP_SDIO_IRQ) {
+		const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
+		u32 clk_en_a_old;
+		u32 clk_en_a;
 
-	if (clk_en_a & clken_low_pwr) {
-		mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
-		mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
-			     SDMMC_CMD_PRV_DAT_WAIT, 0);
+		clk_en_a_old = mci_readl(host, CLKENA);
+
+		if (card->type == MMC_TYPE_SDIO ||
+		    card->type == MMC_TYPE_SD_COMBO) {
+			set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
+			clk_en_a = clk_en_a_old & ~clken_low_pwr;
+		} else {
+			clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
+			clk_en_a = clk_en_a_old | clken_low_pwr;
+		}
+
+		if (clk_en_a != clk_en_a_old) {
+			mci_writel(host, CLKENA, clk_en_a);
+			mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
+				     SDMMC_CMD_PRV_DAT_WAIT, 0);
+		}
 	}
 }
 
@@ -1082,21 +1093,11 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
 
 	/* Enable/disable Slot Specific SDIO interrupt */
 	int_mask = mci_readl(host, INTMASK);
-	if (enb) {
-		/*
-		 * Turn off low power mode if it was enabled.  This is a bit of
-		 * a heavy operation and we disable / enable IRQs a lot, so
-		 * we'll leave low power mode disabled and it will get
-		 * re-enabled again in dw_mci_setup_bus().
-		 */
-		dw_mci_disable_low_power(slot);
-
-		mci_writel(host, INTMASK,
-			   (int_mask | SDMMC_INT_SDIO(slot->id)));
-	} else {
-		mci_writel(host, INTMASK,
-			   (int_mask & ~SDMMC_INT_SDIO(slot->id)));
-	}
+	if (enb)
+		int_mask |= SDMMC_INT_SDIO(slot->id);
+	else
+		int_mask &= ~SDMMC_INT_SDIO(slot->id);
+	mci_writel(host, INTMASK, int_mask);
 }
 
 static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
@@ -1140,6 +1141,7 @@ static const struct mmc_host_ops dw_mci_ops = {
 	.get_cd			= dw_mci_get_cd,
 	.enable_sdio_irq	= dw_mci_enable_sdio_irq,
 	.execute_tuning		= dw_mci_execute_tuning,
+	.init_card		= dw_mci_init_card,
 };
 
 static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 6bf24ab..26d4657 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -227,6 +227,7 @@ struct dw_mci_slot {
 	unsigned long		flags;
 #define DW_MMC_CARD_PRESENT	0
 #define DW_MMC_CARD_NEED_INIT	1
+#define DW_MMC_CARD_NO_LOW_PWR	2
 	int			id;
 	int			last_detect_state;
 };
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
  2013-10-15 22:39 [PATCH 0/2] Prevent races when doing read-modify-write of INTMASK Doug Anderson
  2013-10-15 22:39 ` [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts Doug Anderson
@ 2013-10-15 22:39 ` Doug Anderson
  2013-10-16  9:49   ` James Hogan
  1 sibling, 1 reply; 15+ messages in thread
From: Doug Anderson @ 2013-10-15 22:39 UTC (permalink / raw)
  To: Chris Ball
  Cc: Jaehoon Chung, Seungwon Jeon, James Hogan, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, Doug Anderson, linux-mmc, linux-kernel

We're running into cases where our enabling of the SDIO interrupt in
dw_mmc doesn't actually take effect.  Specifically, adding patch like
this:

 +++ b/drivers/mmc/host/dw_mmc.c
 @@ -1076,6 +1076,9 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)

      mci_writel(host, INTMASK,
           (int_mask | SDMMC_INT_SDIO(slot->id)));
 +    int_mask = mci_readl(host, INTMASK);
 +    if (!(int_mask & SDMMC_INT_SDIO(slot->id)))
 +      dev_err(&mmc->class_dev, "failed to enable sdio irq\n");
    } else {

...actually triggers the error message.  That's because the
dw_mci_enable_sdio_irq() unsafely does a read-modify-write of the
INTMASK register.

We can't just use the standard host->lock since that lock is not irq
safe and mmc_signal_sdio_irq() (called from interrupt context) calls
dw_mci_enable_sdio_irq().  Add a new irq-safe lock to protect INTMASK.

An alternate solution to this is to punt mmc_signal_sdio_irq() to the
tasklet and then protect INTMASK modifications by the standard host
lock.  This seemed like a bit more of a high-latency change.

Reported-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Doug Anderson <dianders@chromium.org>
---
 drivers/mmc/host/dw_mmc.c  | 13 +++++++++++++
 include/linux/mmc/dw_mmc.h |  6 ++++++
 2 files changed, 19 insertions(+)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1b75816..b810654 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -657,6 +657,7 @@ disable:
 
 static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 {
+	unsigned long irqflags;
 	int sg_len;
 	u32 temp;
 
@@ -693,9 +694,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 	mci_writel(host, CTRL, temp);
 
 	/* Disable RX/TX IRQs, let DMA handle it */
+	spin_lock_irqsave(&host->intmask_lock, irqflags);
 	temp = mci_readl(host, INTMASK);
 	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
 	mci_writel(host, INTMASK, temp);
+	spin_unlock_irqrestore(&host->intmask_lock, irqflags);
 
 	host->dma_ops->start(host, sg_len);
 
@@ -704,6 +707,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 
 static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
 {
+	unsigned long irqflags;
 	u32 temp;
 
 	data->error = -EINPROGRESS;
@@ -732,9 +736,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
 		host->part_buf_count = 0;
 
 		mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR);
+
+		spin_lock_irqsave(&host->intmask_lock, irqflags);
 		temp = mci_readl(host, INTMASK);
 		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
 		mci_writel(host, INTMASK, temp);
+		spin_unlock_irqrestore(&host->intmask_lock, irqflags);
 
 		temp = mci_readl(host, CTRL);
 		temp &= ~SDMMC_CTRL_DMA_ENABLE;
@@ -1089,8 +1096,11 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
 {
 	struct dw_mci_slot *slot = mmc_priv(mmc);
 	struct dw_mci *host = slot->host;
+	unsigned long irqflags;
 	u32 int_mask;
 
+	spin_lock_irqsave(&host->intmask_lock, irqflags);
+
 	/* Enable/disable Slot Specific SDIO interrupt */
 	int_mask = mci_readl(host, INTMASK);
 	if (enb)
@@ -1098,6 +1108,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
 	else
 		int_mask &= ~SDMMC_INT_SDIO(slot->id);
 	mci_writel(host, INTMASK, int_mask);
+
+	spin_unlock_irqrestore(&host->intmask_lock, irqflags);
 }
 
 static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
@@ -2500,6 +2512,7 @@ int dw_mci_probe(struct dw_mci *host)
 	host->quirks = host->pdata->quirks;
 
 	spin_lock_init(&host->lock);
+	spin_lock_init(&host->intmask_lock);
 	INIT_LIST_HEAD(&host->queue);
 
 	/*
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 6ce7d2c..002ab56 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -102,6 +102,11 @@ struct mmc_data;
  * @cur_slot, @mrq and @state. These must always be updated
  * at the same time while holding @lock.
  *
+ * @intmask_lock is an irq-safe spinlock protecting the INTMASK register
+ * to allow the interrupt handler to modify it directly.  Held for only long
+ * enough to read-modify-write INTMASK and no other locks are grabbed when
+ * holding this one.
+ *
  * The @mrq field of struct dw_mci_slot is also protected by @lock,
  * and must always be written at the same time as the slot is added to
  * @queue.
@@ -121,6 +126,7 @@ struct mmc_data;
  */
 struct dw_mci {
 	spinlock_t		lock;
+	spinlock_t		intmask_lock;
 	void __iomem		*regs;
 
 	struct scatterlist	*sg;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
  2013-10-15 22:39 ` [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock Doug Anderson
@ 2013-10-16  9:49   ` James Hogan
  2013-10-16 16:43     ` Doug Anderson
  0 siblings, 1 reply; 15+ messages in thread
From: James Hogan @ 2013-10-16  9:49 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Chris Ball, Jaehoon Chung, Seungwon Jeon, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

Hi Doug.

Nice catch.

On 15/10/13 23:39, Doug Anderson wrote:
> We're running into cases where our enabling of the SDIO interrupt in
> dw_mmc doesn't actually take effect.  Specifically, adding patch like
> this:
> 
>  +++ b/drivers/mmc/host/dw_mmc.c
>  @@ -1076,6 +1076,9 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
> 
>       mci_writel(host, INTMASK,
>            (int_mask | SDMMC_INT_SDIO(slot->id)));
>  +    int_mask = mci_readl(host, INTMASK);
>  +    if (!(int_mask & SDMMC_INT_SDIO(slot->id)))
>  +      dev_err(&mmc->class_dev, "failed to enable sdio irq\n");
>     } else {
> 
> ...actually triggers the error message.  That's because the
> dw_mci_enable_sdio_irq() unsafely does a read-modify-write of the
> INTMASK register.
> 
> We can't just use the standard host->lock since that lock is not irq
> safe and mmc_signal_sdio_irq() (called from interrupt context) calls
> dw_mci_enable_sdio_irq().  Add a new irq-safe lock to protect INTMASK.
> 
> An alternate solution to this is to punt mmc_signal_sdio_irq() to the
> tasklet and then protect INTMASK modifications by the standard host
> lock.  This seemed like a bit more of a high-latency change.

A probably lighter-weight alternative to that alternative is to just
make the existing lock irq safe. Has this been considered?

I'm not entirely convinced it's worth having a separate lock rather than
changing the existing one, but the patch still appears to be correct, so:
Reviewed-by: James Hogan <james.hogan@imgtec.com>

Cheers
James


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
  2013-10-16  9:49   ` James Hogan
@ 2013-10-16 16:43     ` Doug Anderson
  2013-10-16 20:23       ` James Hogan
  0 siblings, 1 reply; 15+ messages in thread
From: Doug Anderson @ 2013-10-16 16:43 UTC (permalink / raw)
  To: James Hogan
  Cc: Chris Ball, Jaehoon Chung, Seungwon Jeon, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

James,

On Wed, Oct 16, 2013 at 2:49 AM, James Hogan <james.hogan@imgtec.com> wrote:
>> We can't just use the standard host->lock since that lock is not irq
>> safe and mmc_signal_sdio_irq() (called from interrupt context) calls
>> dw_mci_enable_sdio_irq().  Add a new irq-safe lock to protect INTMASK.
>>
>> An alternate solution to this is to punt mmc_signal_sdio_irq() to the
>> tasklet and then protect INTMASK modifications by the standard host
>> lock.  This seemed like a bit more of a high-latency change.
>
> A probably lighter-weight alternative to that alternative is to just
> make the existing lock irq safe. Has this been considered?
>
> I'm not entirely convinced it's worth having a separate lock rather than
> changing the existing one, but the patch still appears to be correct, so:
> Reviewed-by: James Hogan <james.hogan@imgtec.com>

I did look at that alternate solution and rejected it, but am happy to
send that up if people think it's better.  Here's why I rejected it:

1. It looks like someone has gone through quite a bit of work to _not_
grab the existing lock in the IRQ handler.  The IRQ handler always
pushes all real work over to the tasklet.  I can only assume that they
did this for some good reason and I'd hate to switch it without
knowing for sure why.

2. We might have performance problems if we blindly changed the
existing code to an IRQ-safe spinlock.  We hold the existing
"host->lock" spinlock for the entire duration of
dw_mci_tasklet_func().  That's a pretty intense chunk of code, full of
nested loops (some with 500ms timeouts!), mdelay(20) calls to handle
some errors, etc.  I say "might" because I think that the expected
case is that code runs pretty quickly.  I ran some brief tests on one
system and saw a worst case time of 170us and an common case time of
~10us.  Are we OK with having interrupts disabled for that long?  Are
we OK with having the dw_mci interrupt handler potentially spin on a
spinlock for that long?


I don't think it would be terrible to look at reworking the way that
dw_mmc handles interrupts, but that seems pretty major.


BTW: is the cost of an extra lock actually that high?  ...or are you
talking the cost in terms of code complexity?


-Doug

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
  2013-10-16 16:43     ` Doug Anderson
@ 2013-10-16 20:23       ` James Hogan
  2013-10-18  9:51         ` Jaehoon Chung
  0 siblings, 1 reply; 15+ messages in thread
From: James Hogan @ 2013-10-16 20:23 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Chris Ball, Jaehoon Chung, Seungwon Jeon, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

Hi Doug,

On 16 October 2013 17:43, Doug Anderson <dianders@chromium.org> wrote:
> On Wed, Oct 16, 2013 at 2:49 AM, James Hogan <james.hogan@imgtec.com> wrote:
>>> We can't just use the standard host->lock since that lock is not irq
>>> safe and mmc_signal_sdio_irq() (called from interrupt context) calls
>>> dw_mci_enable_sdio_irq().  Add a new irq-safe lock to protect INTMASK.
>>>
>>> An alternate solution to this is to punt mmc_signal_sdio_irq() to the
>>> tasklet and then protect INTMASK modifications by the standard host
>>> lock.  This seemed like a bit more of a high-latency change.
>>
>> A probably lighter-weight alternative to that alternative is to just
>> make the existing lock irq safe. Has this been considered?
>>
>> I'm not entirely convinced it's worth having a separate lock rather than
>> changing the existing one, but the patch still appears to be correct, so:
>> Reviewed-by: James Hogan <james.hogan@imgtec.com>
>
> I did look at that alternate solution and rejected it, but am happy to
> send that up if people think it's better.  Here's why I rejected it:
>
> 1. It looks like someone has gone through quite a bit of work to _not_
> grab the existing lock in the IRQ handler.  The IRQ handler always
> pushes all real work over to the tasklet.  I can only assume that they
> did this for some good reason and I'd hate to switch it without
> knowing for sure why.
>
> 2. We might have performance problems if we blindly changed the
> existing code to an IRQ-safe spinlock.  We hold the existing
> "host->lock" spinlock for the entire duration of
> dw_mci_tasklet_func().  That's a pretty intense chunk of code, full of
> nested loops (some with 500ms timeouts!), mdelay(20) calls to handle
> some errors, etc.  I say "might" because I think that the expected
> case is that code runs pretty quickly.  I ran some brief tests on one
> system and saw a worst case time of 170us and an common case time of
> ~10us.  Are we OK with having interrupts disabled for that long?  Are
> we OK with having the dw_mci interrupt handler potentially spin on a
> spinlock for that long?
>

Fair enough, I'm convinced now. That code did look pretty heavy when I
looked at it too.

>
> I don't think it would be terrible to look at reworking the way that
> dw_mmc handles interrupts, but that seems pretty major.

Yeh, I suppose at least the potentially large delays are all for
exceptional cases, so it's not a critical problem.

> BTW: is the cost of an extra lock actually that high?

I don't know tbh. In this case the spinlock usage doesn't appear to
actually overlap.

> ...or are you talking the cost in terms of code complexity?

In this case it'd only be a space and code complexity thing I think. I
suppose in some cases the benefit of finer-grained locking is probably
pretty marginal, but there's a good case for it here. It might be
worth renaming the lock to irq_lock or something like that so it's
clear it doesn't have to protect only for INTMASK in the future - up
to you.

Cheers
James

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-15 22:39 ` [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts Doug Anderson
@ 2013-10-18  9:42   ` Jaehoon Chung
  2013-10-18 20:09     ` Doug Anderson
  0 siblings, 1 reply; 15+ messages in thread
From: Jaehoon Chung @ 2013-10-18  9:42 UTC (permalink / raw)
  To: Doug Anderson, Chris Ball
  Cc: Seungwon Jeon, James Hogan, Grant Grundler, Alim Akhtar,
	Abhilash Kesavan, Tomasz Figa, Olof Johansson, Sonny Rao,
	Bing Zhao, linux-mmc, linux-kernel

Hi Doug,

On 10/16/2013 07:39 AM, Doug Anderson wrote:
> In the patch (9623b5b mmc: dw_mmc: Disable low power mode if SDIO
> interrupts are used) I added code that disabled the low power mode of
> dw_mmc when SDIO interrupts are used.  That code worked but always
> felt a little hacky because we ended up disabling low power as a side
> effect of the first enable_sdio_irq() call.  That wouldn't be so bad
> except that disabling low power involves a complicated process of
> writing to the CMD/CMDARG registers and that extra process makes it
> difficult to cleanly the read-modify-write race in
> dw_mci_enable_sdio_irq() (see future patch in the series).
> 
> Change the code to take advantage of the init_card() callback of the
> mmc core to know when an SDIO card has been inserted.  If we've got a
> SDIO card and we're configured to use SDIO Interrupts then turn off
> "low power mode" right away.
> 
> Signed-off-by: Doug Anderson <dianders@chromium.org>
> ---
>  drivers/mmc/host/dw_mmc.c | 68 ++++++++++++++++++++++++-----------------------
>  drivers/mmc/host/dw_mmc.h |  1 +
>  2 files changed, 36 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 0a6a512..1b75816 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -27,6 +27,7 @@
>  #include <linux/stat.h>
>  #include <linux/delay.h>
>  #include <linux/irq.h>
> +#include <linux/mmc/card.h>
>  #include <linux/mmc/host.h>
>  #include <linux/mmc/mmc.h>
>  #include <linux/mmc/sdio.h>
> @@ -822,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
>  
>  		/* enable clock; only low power if no SDIO */
>  		clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
> -		if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id)))
> +		if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags))
>  			clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
>  		mci_writel(host, CLKENA, clk_en_a);
>  
> @@ -1050,27 +1051,37 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
>  	return present;
>  }
>  
> -/*
> - * Disable lower power mode.
> - *
> - * Low power mode will stop the card clock when idle.  According to the
> - * description of the CLKENA register we should disable low power mode
> - * for SDIO cards if we need SDIO interrupts to work.
> - *
> - * This function is fast if low power mode is already disabled.
> - */
> -static void dw_mci_disable_low_power(struct dw_mci_slot *slot)
> +static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
>  {
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
>  	struct dw_mci *host = slot->host;
> -	u32 clk_en_a;
> -	const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
>  
> -	clk_en_a = mci_readl(host, CLKENA);
> +	/*
> +	 * Low power mode will stop the card clock when idle.  According to the
> +	 * description of the CLKENA register we should disable low power mode
> +	 * for SDIO cards if we need SDIO interrupts to work.
> +	 */
> +	if (mmc->caps | MMC_CAP_SDIO_IRQ) {
mmc->caps & MMC_CAP_SDIO_IRQ?
> +		const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
> +		u32 clk_en_a_old;
> +		u32 clk_en_a;
>  
> -	if (clk_en_a & clken_low_pwr) {
> -		mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
> -		mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
> -			     SDMMC_CMD_PRV_DAT_WAIT, 0);
> +		clk_en_a_old = mci_readl(host, CLKENA);
> +
> +		if (card->type == MMC_TYPE_SDIO ||
> +		    card->type == MMC_TYPE_SD_COMBO) {
> +			set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
> +			clk_en_a = clk_en_a_old & ~clken_low_pwr;
> +		} else {
> +			clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
> +			clk_en_a = clk_en_a_old | clken_low_pwr;
When this condition is entered? card->type is always MMC_TYPE_SDIO or MMC_TYPE_SD_COMBO, isn't?

Best Regards,
Jaehoon Chung
> +		}
> +
> +		if (clk_en_a != clk_en_a_old) {
> +			mci_writel(host, CLKENA, clk_en_a);
> +			mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
> +				     SDMMC_CMD_PRV_DAT_WAIT, 0);
> +		}
>  	}
>  }
>  
> @@ -1082,21 +1093,11 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
>  
>  	/* Enable/disable Slot Specific SDIO interrupt */
>  	int_mask = mci_readl(host, INTMASK);
> -	if (enb) {
> -		/*
> -		 * Turn off low power mode if it was enabled.  This is a bit of
> -		 * a heavy operation and we disable / enable IRQs a lot, so
> -		 * we'll leave low power mode disabled and it will get
> -		 * re-enabled again in dw_mci_setup_bus().
> -		 */
> -		dw_mci_disable_low_power(slot);
> -
> -		mci_writel(host, INTMASK,
> -			   (int_mask | SDMMC_INT_SDIO(slot->id)));
> -	} else {
> -		mci_writel(host, INTMASK,
> -			   (int_mask & ~SDMMC_INT_SDIO(slot->id)));
> -	}
> +	if (enb)
> +		int_mask |= SDMMC_INT_SDIO(slot->id);
> +	else
> +		int_mask &= ~SDMMC_INT_SDIO(slot->id);
> +	mci_writel(host, INTMASK, int_mask);
>  }
>  
>  static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
> @@ -1140,6 +1141,7 @@ static const struct mmc_host_ops dw_mci_ops = {
>  	.get_cd			= dw_mci_get_cd,
>  	.enable_sdio_irq	= dw_mci_enable_sdio_irq,
>  	.execute_tuning		= dw_mci_execute_tuning,
> +	.init_card		= dw_mci_init_card,
>  };
>  
>  static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> index 6bf24ab..26d4657 100644
> --- a/drivers/mmc/host/dw_mmc.h
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -227,6 +227,7 @@ struct dw_mci_slot {
>  	unsigned long		flags;
>  #define DW_MMC_CARD_PRESENT	0
>  #define DW_MMC_CARD_NEED_INIT	1
> +#define DW_MMC_CARD_NO_LOW_PWR	2
>  	int			id;
>  	int			last_detect_state;
>  };
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
  2013-10-16 20:23       ` James Hogan
@ 2013-10-18  9:51         ` Jaehoon Chung
  2013-10-18 20:09           ` Doug Anderson
  0 siblings, 1 reply; 15+ messages in thread
From: Jaehoon Chung @ 2013-10-18  9:51 UTC (permalink / raw)
  To: James Hogan, Doug Anderson
  Cc: Chris Ball, Seungwon Jeon, Grant Grundler, Alim Akhtar,
	Abhilash Kesavan, Tomasz Figa, Olof Johansson, Sonny Rao,
	Bing Zhao, linux-mmc, linux-kernel

On 10/17/2013 05:23 AM, James Hogan wrote:
> Hi Doug,
> 
> On 16 October 2013 17:43, Doug Anderson <dianders@chromium.org> wrote:
>> On Wed, Oct 16, 2013 at 2:49 AM, James Hogan <james.hogan@imgtec.com> wrote:
>>>> We can't just use the standard host->lock since that lock is not irq
>>>> safe and mmc_signal_sdio_irq() (called from interrupt context) calls
>>>> dw_mci_enable_sdio_irq().  Add a new irq-safe lock to protect INTMASK.
>>>>
>>>> An alternate solution to this is to punt mmc_signal_sdio_irq() to the
>>>> tasklet and then protect INTMASK modifications by the standard host
>>>> lock.  This seemed like a bit more of a high-latency change.
>>>
>>> A probably lighter-weight alternative to that alternative is to just
>>> make the existing lock irq safe. Has this been considered?
>>>
>>> I'm not entirely convinced it's worth having a separate lock rather than
>>> changing the existing one, but the patch still appears to be correct, so:
>>> Reviewed-by: James Hogan <james.hogan@imgtec.com>
>>
>> I did look at that alternate solution and rejected it, but am happy to
>> send that up if people think it's better.  Here's why I rejected it:
>>
>> 1. It looks like someone has gone through quite a bit of work to _not_
>> grab the existing lock in the IRQ handler.  The IRQ handler always
>> pushes all real work over to the tasklet.  I can only assume that they
>> did this for some good reason and I'd hate to switch it without
>> knowing for sure why.
>>
>> 2. We might have performance problems if we blindly changed the
>> existing code to an IRQ-safe spinlock.  We hold the existing
>> "host->lock" spinlock for the entire duration of
>> dw_mci_tasklet_func().  That's a pretty intense chunk of code, full of
>> nested loops (some with 500ms timeouts!), mdelay(20) calls to handle
>> some errors, etc.  I say "might" because I think that the expected
>> case is that code runs pretty quickly.  I ran some brief tests on one
>> system and saw a worst case time of 170us and an common case time of
>> ~10us.  Are we OK with having interrupts disabled for that long?  Are
>> we OK with having the dw_mci interrupt handler potentially spin on a
>> spinlock for that long?
>>
> 
> Fair enough, I'm convinced now. That code did look pretty heavy when I
> looked at it too.
> 
>>
>> I don't think it would be terrible to look at reworking the way that
>> dw_mmc handles interrupts, but that seems pretty major.
Yes, Reworking is pretty major.
but if we need to rework, i think we can rework it in future.
> 
> Yeh, I suppose at least the potentially large delays are all for
> exceptional cases, so it's not a critical problem.
> 
>> BTW: is the cost of an extra lock actually that high?
> 
> I don't know tbh. In this case the spinlock usage doesn't appear to
> actually overlap.
> 
>> ...or are you talking the cost in terms of code complexity?
> 
> In this case it'd only be a space and code complexity thing I think. I
> suppose in some cases the benefit of finer-grained locking is probably
> pretty marginal, but there's a good case for it here. It might be
> worth renaming the lock to irq_lock or something like that so it's
> clear it doesn't have to protect only for INTMASK in the future - up
> to you.
It seems good that use the irq_lock than intmask_lock. (It's just naming)
> 
> Cheers
> James
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-18  9:42   ` Jaehoon Chung
@ 2013-10-18 20:09     ` Doug Anderson
  2013-10-23 11:25       ` Seungwon Jeon
  0 siblings, 1 reply; 15+ messages in thread
From: Doug Anderson @ 2013-10-18 20:09 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Chris Ball, Seungwon Jeon, James Hogan, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

Jaehoon,

On Fri, Oct 18, 2013 at 2:42 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> -     clk_en_a = mci_readl(host, CLKENA);
>> +     /*
>> +      * Low power mode will stop the card clock when idle.  According to the
>> +      * description of the CLKENA register we should disable low power mode
>> +      * for SDIO cards if we need SDIO interrupts to work.
>> +      */
>> +     if (mmc->caps | MMC_CAP_SDIO_IRQ) {
> mmc->caps & MMC_CAP_SDIO_IRQ?

Wow, that was an embarrassing one.  Thanks.

>> +             const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
>> +             u32 clk_en_a_old;
>> +             u32 clk_en_a;
>>
>> -     if (clk_en_a & clken_low_pwr) {
>> -             mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
>> -             mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
>> -                          SDMMC_CMD_PRV_DAT_WAIT, 0);
>> +             clk_en_a_old = mci_readl(host, CLKENA);
>> +
>> +             if (card->type == MMC_TYPE_SDIO ||
>> +                 card->type == MMC_TYPE_SD_COMBO) {
>> +                     set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
>> +                     clk_en_a = clk_en_a_old & ~clken_low_pwr;
>> +             } else {
>> +                     clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
>> +                     clk_en_a = clk_en_a_old | clken_low_pwr;
> When this condition is entered? card->type is always MMC_TYPE_SDIO or MMC_TYPE_SD_COMBO, isn't?

Ugh, that's not intuitive.  This callback is only called for SDIO
cards and not MMC/SD cards?  That means if you plug in an SDIO card
and then eject it and plug in an SD card you won't get to low power.
Hrm.

I dug around a bit and couldn't find a better way to do this and then
I realized that the other user of the init_card() callback has the
same bug, so for the next version of the series I'm proposing a fix
for mmc core to add this for all types.  If you have a better
suggestion, I'm all ears.

-Doug

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
  2013-10-18  9:51         ` Jaehoon Chung
@ 2013-10-18 20:09           ` Doug Anderson
  0 siblings, 0 replies; 15+ messages in thread
From: Doug Anderson @ 2013-10-18 20:09 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: James Hogan, Chris Ball, Seungwon Jeon, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

Jaehoon / James

On Fri, Oct 18, 2013 at 2:51 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> In this case it'd only be a space and code complexity thing I think. I
>> suppose in some cases the benefit of finer-grained locking is probably
>> pretty marginal, but there's a good case for it here. It might be
>> worth renaming the lock to irq_lock or something like that so it's
>> clear it doesn't have to protect only for INTMASK in the future - up
>> to you.
> It seems good that use the irq_lock than intmask_lock. (It's just naming)

Done in v2.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-18 20:09     ` Doug Anderson
@ 2013-10-23 11:25       ` Seungwon Jeon
  2013-10-24  7:28         ` Doug Anderson
  0 siblings, 1 reply; 15+ messages in thread
From: Seungwon Jeon @ 2013-10-23 11:25 UTC (permalink / raw)
  To: 'Doug Anderson', 'Jaehoon Chung'
  Cc: 'Chris Ball', 'James Hogan',
	'Grant Grundler', 'Alim Akhtar',
	'Abhilash Kesavan', 'Tomasz Figa',
	'Olof Johansson', 'Sonny Rao',
	'Bing Zhao',
	linux-mmc, linux-kernel

Hi Doug,

This change looks good.
There's comment below.

On Sat, October 19, 2013, Doug Anderson wrote:
> Jaehoon,
> 
> On Fri, Oct 18, 2013 at 2:42 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> >> -     clk_en_a = mci_readl(host, CLKENA);
> >> +     /*
> >> +      * Low power mode will stop the card clock when idle.  According to the
> >> +      * description of the CLKENA register we should disable low power mode
> >> +      * for SDIO cards if we need SDIO interrupts to work.
> >> +      */
> >> +     if (mmc->caps | MMC_CAP_SDIO_IRQ) {
> > mmc->caps & MMC_CAP_SDIO_IRQ?
> 
> Wow, that was an embarrassing one.  Thanks.
> 
> >> +             const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
> >> +             u32 clk_en_a_old;
> >> +             u32 clk_en_a;
> >>
> >> -     if (clk_en_a & clken_low_pwr) {
> >> -             mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
> >> -             mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
> >> -                          SDMMC_CMD_PRV_DAT_WAIT, 0);
> >> +             clk_en_a_old = mci_readl(host, CLKENA);
> >> +
> >> +             if (card->type == MMC_TYPE_SDIO ||
> >> +                 card->type == MMC_TYPE_SD_COMBO) {
&& card->quirks & MMC_QUIRK_BROKEN_CLK_GATING
How about considering MMC_QUIRK_BROKEN_CLK_GATING?
Some sdio device can work with gating clock.
For this, mmc_fixup_device() should be called prior to init_card() in core(sdio.c).
I guess you found that.

Thanks,
Seungwon Jeon

> >> +                     set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
> >> +                     clk_en_a = clk_en_a_old & ~clken_low_pwr;
> >> +             } else {
> >> +                     clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
> >> +                     clk_en_a = clk_en_a_old | clken_low_pwr;
> > When this condition is entered? card->type is always MMC_TYPE_SDIO or MMC_TYPE_SD_COMBO, isn't?
> 
> Ugh, that's not intuitive.  This callback is only called for SDIO
> cards and not MMC/SD cards?  That means if you plug in an SDIO card
> and then eject it and plug in an SD card you won't get to low power.
> Hrm.
> 
> I dug around a bit and couldn't find a better way to do this and then
> I realized that the other user of the init_card() callback has the
> same bug, so for the next version of the series I'm proposing a fix
> for mmc core to add this for all types.  If you have a better
> suggestion, I'm all ears.
> 
> -Doug
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-23 11:25       ` Seungwon Jeon
@ 2013-10-24  7:28         ` Doug Anderson
  2013-10-25  9:29           ` Seungwon Jeon
  0 siblings, 1 reply; 15+ messages in thread
From: Doug Anderson @ 2013-10-24  7:28 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: Jaehoon Chung, Chris Ball, James Hogan, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

Seungwon,

On Wed, Oct 23, 2013 at 12:25 PM, Seungwon Jeon <tgih.jun@samsung.com> wrote:
>> >> +             if (card->type == MMC_TYPE_SDIO ||
>> >> +                 card->type == MMC_TYPE_SD_COMBO) {
> && card->quirks & MMC_QUIRK_BROKEN_CLK_GATING
> How about considering MMC_QUIRK_BROKEN_CLK_GATING?
> Some sdio device can work with gating clock.
> For this, mmc_fixup_device() should be called prior to init_card() in core(sdio.c).
> I guess you found that.

By SDIO devices, are you referring to actual SDIO cards or some
implementations of dw_mmc?

As far as I understand in the CLKENA description in the generic
documentation from Synopsys it say that for SDIO cards you must not
stop the clock if interrupts must be detected.  To me, that means that
all dw_mmc implementations require this change if they support SDIO
interrupts (hence checking for MMC_CAP_SDIO_IRQ).

I guess I did make the assumption in this change that all (reasonable)
SDIO cards would be using SDIO interrupts if they are available.  If
we could find out ahead of time if a given SDIO driver was planning to
use interrupts we could do better.  The old solution did better in
this way and we could probably make it work (and still fix the
read-modify-write race) if we thought this was really important.  The
code gets a little more twisted to try to avoid holding the IRQ-safe
spinlock while updating the clock, but I could attempt it...


NOTE: We've recently found that there are still occasions when we lose
SDIO interrupts with dw_mmc and our Marvell WiFi driver, especially
when those interrupts happen back-to-back.  That problem appears
unrelated to this one (it's what Bing was investigating when he found
the original race).  Anyone that has any thoughts, please let me
know...

-Doug

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-24  7:28         ` Doug Anderson
@ 2013-10-25  9:29           ` Seungwon Jeon
  2013-10-28 22:39             ` Doug Anderson
  0 siblings, 1 reply; 15+ messages in thread
From: Seungwon Jeon @ 2013-10-25  9:29 UTC (permalink / raw)
  To: 'Doug Anderson'
  Cc: 'Jaehoon Chung', 'Chris Ball',
	'James Hogan', 'Grant Grundler',
	'Alim Akhtar', 'Abhilash Kesavan',
	'Tomasz Figa', 'Olof Johansson',
	'Sonny Rao', 'Bing Zhao',
	linux-mmc, linux-kernel

On Thu, October 24, 2013, Doug Anderson wrote:
> Seungwon,
> 
> On Wed, Oct 23, 2013 at 12:25 PM, Seungwon Jeon <tgih.jun@samsung.com> wrote:
> >> >> +             if (card->type == MMC_TYPE_SDIO ||
> >> >> +                 card->type == MMC_TYPE_SD_COMBO) {
> > && card->quirks & MMC_QUIRK_BROKEN_CLK_GATING
> > How about considering MMC_QUIRK_BROKEN_CLK_GATING?
> > Some sdio device can work with gating clock.
> > For this, mmc_fixup_device() should be called prior to init_card() in core(sdio.c).
> > I guess you found that.
> 
> By SDIO devices, are you referring to actual SDIO cards or some
> implementations of dw_mmc?
> 
> As far as I understand in the CLKENA description in the generic
> documentation from Synopsys it say that for SDIO cards you must not
> stop the clock if interrupts must be detected.  To me, that means that
> all dw_mmc implementations require this change if they support SDIO
> interrupts (hence checking for MMC_CAP_SDIO_IRQ).

CLKENA description in manual means that host controller can't detect the SDIO interrupt signal
or wifi device can't generate the interrupt without clock?
Host controller based on Synopsys supports asynchronous interrupts. It seems to depend on wifi Device.
If host can do and  wifi device can also work with clock gating, we may enable low-power mode.

For MMC_QUIRK_BROKEN_CLK_GATING, I referred the code in 'mmc/core/quirks.c'
In addition, although host can support MMC_CAP_SDIO_IRQ, some wifi drivers use
OOB(Out-of-band) interrupt. That means host can apply clock gating to reduce
power consumption.

Thanks,
Seungwon Jeon

> 
> I guess I did make the assumption in this change that all (reasonable)
> SDIO cards would be using SDIO interrupts if they are available.  If
> we could find out ahead of time if a given SDIO driver was planning to
> use interrupts we could do better.  The old solution did better in
> this way and we could probably make it work (and still fix the
> read-modify-write race) if we thought this was really important.  The
> code gets a little more twisted to try to avoid holding the IRQ-safe
> spinlock while updating the clock, but I could attempt it...
> 
> 
> NOTE: We've recently found that there are still occasions when we lose
> SDIO interrupts with dw_mmc and our Marvell WiFi driver, especially
> when those interrupts happen back-to-back.  That problem appears
> unrelated to this one (it's what Bing was investigating when he found
> the original race).  Anyone that has any thoughts, please let me
> know...
> 
> -Doug
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-25  9:29           ` Seungwon Jeon
@ 2013-10-28 22:39             ` Doug Anderson
  2013-11-01  5:23               ` Seungwon Jeon
  0 siblings, 1 reply; 15+ messages in thread
From: Doug Anderson @ 2013-10-28 22:39 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: Jaehoon Chung, Chris Ball, James Hogan, Grant Grundler,
	Alim Akhtar, Abhilash Kesavan, Tomasz Figa, Olof Johansson,
	Sonny Rao, Bing Zhao, linux-mmc, linux-kernel

Seungwon,

On Fri, Oct 25, 2013 at 2:29 AM, Seungwon Jeon <tgih.jun@samsung.com> wrote:
>> By SDIO devices, are you referring to actual SDIO cards or some
>> implementations of dw_mmc?
>>
>> As far as I understand in the CLKENA description in the generic
>> documentation from Synopsys it say that for SDIO cards you must not
>> stop the clock if interrupts must be detected.  To me, that means that
>> all dw_mmc implementations require this change if they support SDIO
>> interrupts (hence checking for MMC_CAP_SDIO_IRQ).
>
> CLKENA description in manual means that host controller can't detect the SDIO interrupt signal
> or wifi device can't generate the interrupt without clock?

My reading of the "if interrupts must be detected" in the manual
implies that that interrupts simply can't be detected by the
controller.


> Host controller based on Synopsys supports asynchronous interrupts. It seems to depend on wifi Device.
> If host can do and  wifi device can also work with clock gating, we may enable low-power mode.

Ah, interesting!  I wish I had known about this earlier and we could
have actually used it in our designs.  Please correct me if I'm wrong
but...

It looks like asynchronous interrupts is when you use a separate INT#
line for your SDIO interrupts and is available only for eSDIO
(embedded SDIO?), right.  ...so that means it more a property of the
board rather than the card itself.  In other words: to use
asynchronous interrupts you need to be on a SoC that supports the INT#
line, you need to have it wired up to an eSDIO module, and you need
the eSDIO card to support it.

Assuming that I understand all of the above I'd suggest (in a future
patch) that we add a property like 'dedicated-sdio-irq' to device
trees.  If we see this property AND we don't see
MMC_QUIRK_BROKEN_CLK_GATING then we know we don't need to disable
clock gating.

Does that sound right?  If so I'd still love to land my patch and we
could add the extra logic in a separate patch.


> For MMC_QUIRK_BROKEN_CLK_GATING, I referred the code in 'mmc/core/quirks.c'
> In addition, although host can support MMC_CAP_SDIO_IRQ, some wifi drivers use
> OOB(Out-of-band) interrupt. That means host can apply clock gating to reduce
> power consumption.

I think OOB interrupt is the same as asynchronous interrupt, but if
I'm wrong please correct me.

-Doug

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts
  2013-10-28 22:39             ` Doug Anderson
@ 2013-11-01  5:23               ` Seungwon Jeon
  0 siblings, 0 replies; 15+ messages in thread
From: Seungwon Jeon @ 2013-11-01  5:23 UTC (permalink / raw)
  To: 'Doug Anderson'
  Cc: 'Jaehoon Chung', 'Chris Ball',
	'James Hogan', 'Grant Grundler',
	'Alim Akhtar', 'Abhilash Kesavan',
	'Tomasz Figa', 'Olof Johansson',
	'Sonny Rao', 'Bing Zhao',
	linux-mmc, linux-kernel

On Tue, October 29, 2013, Doug Anderson wrote
> Seungwon,
> 
> On Fri, Oct 25, 2013 at 2:29 AM, Seungwon Jeon <tgih.jun@samsung.com> wrote:
> >> By SDIO devices, are you referring to actual SDIO cards or some
> >> implementations of dw_mmc?
> >>
> >> As far as I understand in the CLKENA description in the generic
> >> documentation from Synopsys it say that for SDIO cards you must not
> >> stop the clock if interrupts must be detected.  To me, that means that
> >> all dw_mmc implementations require this change if they support SDIO
> >> interrupts (hence checking for MMC_CAP_SDIO_IRQ).
> >
> > CLKENA description in manual means that host controller can't detect the SDIO interrupt signal
> > or wifi device can't generate the interrupt without clock?
> 
> My reading of the "if interrupts must be detected" in the manual
> implies that that interrupts simply can't be detected by the
> controller.
I just wanted to know your opinion because I was not convinced that.
But, as far as I know, interrupt is generated by device with clock sync.
If clock is stopped, device may not issue interrupt.
This is the reason that interrupt is not detected.
Ok. Anyway, clock should be required for synchronous interrupt.

> 
> 
> > Host controller based on Synopsys supports asynchronous interrupts. It seems to depend on wifi
> Device.
> > If host can do and  wifi device can also work with clock gating, we may enable low-power mode.
> 
> Ah, interesting!  I wish I had known about this earlier and we could
> have actually used it in our designs.  Please correct me if I'm wrong
> but...
> 
> It looks like asynchronous interrupts is when you use a separate INT#
> line for your SDIO interrupts and is available only for eSDIO
> (embedded SDIO?), right.  ...so that means it more a property of the
> board rather than the card itself.  In other words: to use
> asynchronous interrupts you need to be on a SoC that supports the INT#
> line, you need to have it wired up to an eSDIO module, and you need
> the eSDIO card to support it.
Right. But we cannot say without the device which supports INT#.
For asynchronous interrupt, device should be mounted on target board.

> 
> Assuming that I understand all of the above I'd suggest (in a future
> patch) that we add a property like 'dedicated-sdio-irq' to device
> trees.  If we see this property AND we don't see
> MMC_QUIRK_BROKEN_CLK_GATING then we know we don't need to disable
> clock gating.
> 
> Does that sound right?  If so I'd still love to land my patch and we
> could add the extra logic in a separate patch.
Ok. I like it. Will you send it with this series?
If not, existing MMC_QUIRK_BROKEN_CLK_GATING could be considered at this time.
And, could you modify your comment message more definitely?

> 
> 
> > For MMC_QUIRK_BROKEN_CLK_GATING, I referred the code in 'mmc/core/quirks.c'
> > In addition, although host can support MMC_CAP_SDIO_IRQ, some wifi drivers use
> > OOB(Out-of-band) interrupt. That means host can apply clock gating to reduce
> > power consumption.
> 
> I think OOB interrupt is the same as asynchronous interrupt, but if
> I'm wrong please correct me.
Yes. Eventually both mechanisms are asynchronous.
Additionally, OOB I mentioned comes from some wlan driver commit & implementation.
I guess it doesn't indicate OOB of SDIO specification 4.0 and it's not same.

Thanks,
Seungwon Jeon



^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2013-11-01  5:23 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-15 22:39 [PATCH 0/2] Prevent races when doing read-modify-write of INTMASK Doug Anderson
2013-10-15 22:39 ` [PATCH 1/2] mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts Doug Anderson
2013-10-18  9:42   ` Jaehoon Chung
2013-10-18 20:09     ` Doug Anderson
2013-10-23 11:25       ` Seungwon Jeon
2013-10-24  7:28         ` Doug Anderson
2013-10-25  9:29           ` Seungwon Jeon
2013-10-28 22:39             ` Doug Anderson
2013-11-01  5:23               ` Seungwon Jeon
2013-10-15 22:39 ` [PATCH 2/2] mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock Doug Anderson
2013-10-16  9:49   ` James Hogan
2013-10-16 16:43     ` Doug Anderson
2013-10-16 20:23       ` James Hogan
2013-10-18  9:51         ` Jaehoon Chung
2013-10-18 20:09           ` Doug Anderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).