diff mbox

[1/3] mmc: omap_hsmmc: Enable SDIO IRQ.

Message ID 1384200405-8402-2-git-send-email-afenkart@gmail.com
State Superseded, archived
Headers show

Commit Message

Andreas Fenkart Nov. 11, 2013, 8:06 p.m. UTC
For now, only support SDIO interrupt if we are booted with
DT. This is because some platforms need special quirks. And
we don't want to add new legacy mux platform init code
callbacks any longer as we are moving to DT based booting
anyways.

Broken hardware, missing the swakueup line, should fallback
to polling, by setting 'ti,quirk-swakup-missing' in the
device tree. Otherwise pending SDIO IRQ are not detected
while in suspend. This affects am33xx processors.

For the DT-Binding portion:
Reviewed-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Kumar Gala <galak@codeaurora.org>
Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
---
 .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   18 ++++
 drivers/mmc/host/omap_hsmmc.c                      |   86 ++++++++++++++++++--
 2 files changed, 95 insertions(+), 9 deletions(-)

Comments

Michael Nazzareno Trimarchi Nov. 12, 2013, 11:23 a.m. UTC | #1
Hi Andreas

On Mon, Nov 11, 2013 at 9:06 PM, Andreas Fenkart <afenkart@gmail.com> wrote:
> For now, only support SDIO interrupt if we are booted with
> DT. This is because some platforms need special quirks. And
> we don't want to add new legacy mux platform init code
> callbacks any longer as we are moving to DT based booting
> anyways.
>
> Broken hardware, missing the swakueup line, should fallback
> to polling, by setting 'ti,quirk-swakup-missing' in the
> device tree. Otherwise pending SDIO IRQ are not detected
> while in suspend. This affects am33xx processors.
>
> For the DT-Binding portion:
> Reviewed-by: Grant Likely <grant.likely@secretlab.ca>
> Acked-by: Kumar Gala <galak@codeaurora.org>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
> ---

I think that you should add patch revision, change log description.

Michael

>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   18 ++++
>  drivers/mmc/host/omap_hsmmc.c                      |   86 ++++++++++++++++++--
>  2 files changed, 95 insertions(+), 9 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> index ed271fc..1136e6b 100644
> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> @@ -20,6 +20,24 @@ ti,dual-volt: boolean, supports dual voltage cards
>  ti,non-removable: non-removable slot (like eMMC)
>  ti,needs-special-reset: Requires a special softreset sequence
>  ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed
> +ti,quirk-swakup-missing: SOC missing the swakeup line, will not detect
> +SDIO irq while in suspend. Fallback to polling. Affected chips are
> +am335x,
> +
> +                            ------
> +                            | PRCM |
> +                             ------
> +                              ^ |
> +                      swakeup | | fclk
> +                              | v
> +      ------                -------               -----
> +     | card | -- CIRQ -->  | hsmmc | -- IRQ -->  | CPU |
> +      ------                -------               -----
> +
> +In suspend the fclk is off and the module is disfunctional. Even
> +register reads will fail. A small logic in the host will request fclk
> +restore, when an external event is detected. Once the clock is
> +restored, the host detects the event normally.
>
>  Example:
>         mmc1: mmc@0x4809c000 {
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index b392130..6b0ec55 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -130,6 +130,7 @@ static void apply_clk_hack(struct device *dev)
>  #define TC_EN                  (1 << 1)
>  #define BWR_EN                 (1 << 4)
>  #define BRR_EN                 (1 << 5)
> +#define CIRQ_EN                        (1 << 8)
>  #define ERR_EN                 (1 << 15)
>  #define CTO_EN                 (1 << 16)
>  #define CCRC_EN                        (1 << 17)
> @@ -210,6 +211,9 @@ struct omap_hsmmc_host {
>         int                     reqs_blocked;
>         int                     use_reg;
>         int                     req_in_progress;
> +       int                     flags;
> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 0)        /* SDIO irq enabled */
> +
>         struct omap_hsmmc_next  next_data;
>         struct  omap_mmc_platform_data  *pdata;
>  };
> @@ -490,27 +494,40 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
>  static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
>                                   struct mmc_command *cmd)
>  {
> -       unsigned int irq_mask;
> +       u32 irq_mask = INT_EN_MASK;
> +       unsigned long flags;
>
>         if (host->use_dma)
> -               irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
> -       else
> -               irq_mask = INT_EN_MASK;
> +               irq_mask &= ~(BRR_EN | BWR_EN);
>
>         /* Disable timeout for erases */
>         if (cmd->opcode == MMC_ERASE)
>                 irq_mask &= ~DTO_EN;
>
> +       spin_lock_irqsave(&host->irq_lock, flags);
>         OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>         OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
> +
> +       /* latch pending CIRQ, but don't signal */
> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
> +               irq_mask |= CIRQ_EN;
>         OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>  }
>
>  static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
>  {
> -       OMAP_HSMMC_WRITE(host->base, ISE, 0);
> -       OMAP_HSMMC_WRITE(host->base, IE, 0);
> +       u32 irq_mask = 0;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       /* no transfer running, need to signal cirq if enabled */
> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
> +               irq_mask |= CIRQ_EN;
> +       OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
> +       OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
>         OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>  }
>
>  /* Calculate divisor for the given clock frequency */
> @@ -1067,8 +1084,12 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>         int status;
>
>         status = OMAP_HSMMC_READ(host->base, STAT);
> -       while (status & INT_EN_MASK && host->req_in_progress) {
> -               omap_hsmmc_do_irq(host, status);
> +       while (status & (INT_EN_MASK | CIRQ_EN)) {
> +               if (host->req_in_progress)
> +                       omap_hsmmc_do_irq(host, status);
> +
> +               if (status & CIRQ_EN)
> +                       mmc_signal_sdio_irq(host->mmc);
>
>                 /* Flush posted write */
>                 status = OMAP_HSMMC_READ(host->base, STAT);
> @@ -1583,6 +1604,37 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
>                 mmc_slot(host).init_card(card);
>  }
>
> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
> +{
> +       struct omap_hsmmc_host *host = mmc_priv(mmc);
> +       u32 irq_mask;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +
> +       irq_mask = OMAP_HSMMC_READ(host->base, ISE);
> +       if (enable) {
> +               host->flags |= HSMMC_SDIO_IRQ_ENABLED;
> +               irq_mask |= CIRQ_EN;
> +       } else {
> +               host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
> +               irq_mask &= ~CIRQ_EN;
> +       }
> +       OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
> +
> +       /*
> +        * if enable, piggy back detection on current request
> +        * but always disable immediately
> +        */
> +       if (!host->req_in_progress || !enable)
> +               OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
> +
> +       /* flush posted write */
> +       OMAP_HSMMC_READ(host->base, IE);
> +
> +       spin_unlock_irqrestore(&host->irq_lock, flags);
> +}
> +
>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>  {
>         u32 hctl, capa, value;
> @@ -1635,7 +1687,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
>         .get_cd = omap_hsmmc_get_cd,
>         .get_ro = omap_hsmmc_get_ro,
>         .init_card = omap_hsmmc_init_card,
> -       /* NYET -- enable_sdio_irq */
> +       .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>  };
>
>  #ifdef CONFIG_DEBUG_FS
> @@ -2021,6 +2073,22 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>                 dev_warn(&pdev->dev,
>                         "pins are not configured from the driver\n");
>
> +       /*
> +        * For now, only support SDIO interrupt if we are booted with
> +        * DT. This is because some platforms need special quirks. And
> +        * we don't want to add new legacy mux platform init code
> +        * callbacks any longer as we are moving to DT based booting
> +        * anyways.
> +        */
> +       if (pdev->dev.of_node) {
> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
> +               if (of_find_property(host->dev->of_node,
> +                                    "ti,quirk-swakeup-missing", NULL)) {
> +                       /* no wakeup from deeper power states, use polling */
> +                       mmc->caps &= ~MMC_CAP_SDIO_IRQ;
> +               }
> +       }
> +
>         omap_hsmmc_protect_card(host);
>
>         mmc_add_host(mmc);
> --
> 1.7.10.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
balajitk@ti.com Nov. 12, 2013, 5:22 p.m. UTC | #2
On Tuesday 12 November 2013 01:36 AM, Andreas Fenkart wrote:
> For now, only support SDIO interrupt if we are booted with
> DT. This is because some platforms need special quirks. And
> we don't want to add new legacy mux platform init code
> callbacks any longer as we are moving to DT based booting
> anyways.
>
> Broken hardware, missing the swakueup line, should fallback
> to polling, by setting 'ti,quirk-swakup-missing' in the
> device tree. Otherwise pending SDIO IRQ are not detected
> while in suspend. This affects am33xx processors.
>
> For the DT-Binding portion:
> Reviewed-by: Grant Likely <grant.likely@secretlab.ca>
> Acked-by: Kumar Gala <galak@codeaurora.org>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
> ---
>   .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   18 ++++
>   drivers/mmc/host/omap_hsmmc.c                      |   86 ++++++++++++++++++--
>   2 files changed, 95 insertions(+), 9 deletions(-)
>

> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
> +{
> +	struct omap_hsmmc_host *host = mmc_priv(mmc);
> +	u32 irq_mask;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&host->irq_lock, flags);
> +
Hi Andreas,

Thanks for the patch,
You don't check for runtime pm status here before read/write to HSMMC registers,
can you please explain ?

> +	irq_mask = OMAP_HSMMC_READ(host->base, ISE);
> +	if (enable) {
> +		host->flags |= HSMMC_SDIO_IRQ_ENABLED;
> +		irq_mask |= CIRQ_EN;
> +	} else {
> +		host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
> +		irq_mask &= ~CIRQ_EN;
> +	}
> +	OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
> +
> +	/*
> +	 * if enable, piggy back detection on current request
> +	 * but always disable immediately
> +	 */
> +	if (!host->req_in_progress || !enable)
> +		OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
> +
> +	/* flush posted write */
> +	OMAP_HSMMC_READ(host->base, IE);
> +
> +	spin_unlock_irqrestore(&host->irq_lock, flags);
> +}
> +
>   static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>   {
>   	u32 hctl, capa, value;
> @@ -1635,7 +1687,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
>   	.get_cd = omap_hsmmc_get_cd,
>   	.get_ro = omap_hsmmc_get_ro,
>   	.init_card = omap_hsmmc_init_card,
> -	/* NYET -- enable_sdio_irq */
> +	.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>   };
>
>   #ifdef CONFIG_DEBUG_FS
> @@ -2021,6 +2073,22 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>   		dev_warn(&pdev->dev,
>   			"pins are not configured from the driver\n");
>
> +	/*
> +	 * For now, only support SDIO interrupt if we are booted with
> +	 * DT. This is because some platforms need special quirks. And
> +	 * we don't want to add new legacy mux platform init code
> +	 * callbacks any longer as we are moving to DT based booting
> +	 * anyways.
> +	 */
> +	if (pdev->dev.of_node) {
> +		mmc->caps |= MMC_CAP_SDIO_IRQ;
> +		if (of_find_property(host->dev->of_node,
> +				     "ti,quirk-swakeup-missing", NULL)) {
> +			/* no wakeup from deeper power states, use polling */
> +			mmc->caps &= ~MMC_CAP_SDIO_IRQ;
> +		}
> +	}
> +
>   	omap_hsmmc_protect_card(host);
>
>   	mmc_add_host(mmc);
>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andreas Fenkart Nov. 14, 2013, 9:09 a.m. UTC | #3
2013/11/12 Balaji T K <balajitk@ti.com>:
> On Tuesday 12 November 2013 01:36 AM, Andreas Fenkart wrote:
>>
>> For now, only support SDIO interrupt if we are booted with
>> DT. This is because some platforms need special quirks. And
>> we don't want to add new legacy mux platform init code
>> callbacks any longer as we are moving to DT based booting
>> anyways.
>>
>> Broken hardware, missing the swakueup line, should fallback
>> to polling, by setting 'ti,quirk-swakup-missing' in the
>> device tree. Otherwise pending SDIO IRQ are not detected
>> while in suspend. This affects am33xx processors.
>>
>> For the DT-Binding portion:
>> Reviewed-by: Grant Likely <grant.likely@secretlab.ca>
>> Acked-by: Kumar Gala <galak@codeaurora.org>
>> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>> ---
>>   .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   18 ++++
>>   drivers/mmc/host/omap_hsmmc.c                      |   86
>> ++++++++++++++++++--
>>   2 files changed, 95 insertions(+), 9 deletions(-)
>>
>
>> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
>> +{
>> +       struct omap_hsmmc_host *host = mmc_priv(mmc);
>> +       u32 irq_mask;
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&host->irq_lock, flags);
>> +
>
> Hi Andreas,
>
> Thanks for the patch,
> You don't check for runtime pm status here before read/write to HSMMC
> registers,
> can you please explain ?


The fclk  must be running when you end up here.
- sdio_irq_thread calls mmc_claim_host which calls pm_runtime_get in turn.
- upper level drivers (e.g. wifi) call mmc_claim_host before interacting with
  the host.
- in interrupt context, fclk must be on otherwise irq detection wouldn't work.

FYI:
In a previous patch for am335x with swakeup line missing, this function was
called from gpio irq context directly. There it could have happened that the
fclk was not running and your register accesses caused a SIGBUS.
With this version of the patch the gpio irg handler only wakes up the the host
with pm_request_resume. Actual sdio irq processing is done in a 2nd interrupt
triggered by mmc host itself. This models the behaviour, when your chip has
the swakeup line.
Assuming SDIO irq while in pm_suspend is rare followed by burst of SDIO
irqs. Then the additional delay of the 2nd interrupt would vanish in
the measuring
noise. Hence I chose this solution because it's simpler and less error prone.

>
>
>> +       irq_mask = OMAP_HSMMC_READ(host->base, ISE);
>> +       if (enable) {
>> +               host->flags |= HSMMC_SDIO_IRQ_ENABLED;
>> +               irq_mask |= CIRQ_EN;
>> +       } else {
>> +               host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
>> +               irq_mask &= ~CIRQ_EN;
>> +       }
>> +       OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
>> +
>> +       /*
>> +        * if enable, piggy back detection on current request
>> +        * but always disable immediately
>> +        */
>> +       if (!host->req_in_progress || !enable)
>> +               OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
>> +
>> +       /* flush posted write */
>> +       OMAP_HSMMC_READ(host->base, IE);
>> +
>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>> +}
>> +
>>   static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>>   {
>>         u32 hctl, capa, value;
>> @@ -1635,7 +1687,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
>>         .get_cd = omap_hsmmc_get_cd,
>>         .get_ro = omap_hsmmc_get_ro,
>>         .init_card = omap_hsmmc_init_card,
>> -       /* NYET -- enable_sdio_irq */
>> +       .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>   };
>>
>>   #ifdef CONFIG_DEBUG_FS
>> @@ -2021,6 +2073,22 @@ static int omap_hsmmc_probe(struct platform_device
>> *pdev)
>>                 dev_warn(&pdev->dev,
>>                         "pins are not configured from the driver\n");
>>
>> +       /*
>> +        * For now, only support SDIO interrupt if we are booted with
>> +        * DT. This is because some platforms need special quirks. And
>> +        * we don't want to add new legacy mux platform init code
>> +        * callbacks any longer as we are moving to DT based booting
>> +        * anyways.
>> +        */
>> +       if (pdev->dev.of_node) {
>> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
>> +               if (of_find_property(host->dev->of_node,
>> +                                    "ti,quirk-swakeup-missing", NULL)) {
>> +                       /* no wakeup from deeper power states, use polling
>> */
>> +                       mmc->caps &= ~MMC_CAP_SDIO_IRQ;
>> +               }
>> +       }
>> +
>>         omap_hsmmc_protect_card(host);
>>
>>         mmc_add_host(mmc);
>>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index ed271fc..1136e6b 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -20,6 +20,24 @@  ti,dual-volt: boolean, supports dual voltage cards
 ti,non-removable: non-removable slot (like eMMC)
 ti,needs-special-reset: Requires a special softreset sequence
 ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed
+ti,quirk-swakup-missing: SOC missing the swakeup line, will not detect
+SDIO irq while in suspend. Fallback to polling. Affected chips are
+am335x,
+
+                            ------
+                            | PRCM |
+                             ------
+                              ^ |
+                      swakeup | | fclk
+                              | v
+      ------                -------               -----
+     | card | -- CIRQ -->  | hsmmc | -- IRQ -->  | CPU |
+      ------                -------               -----
+
+In suspend the fclk is off and the module is disfunctional. Even
+register reads will fail. A small logic in the host will request fclk
+restore, when an external event is detected. Once the clock is
+restored, the host detects the event normally.
 
 Example:
 	mmc1: mmc@0x4809c000 {
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b392130..6b0ec55 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -130,6 +130,7 @@  static void apply_clk_hack(struct device *dev)
 #define TC_EN			(1 << 1)
 #define BWR_EN			(1 << 4)
 #define BRR_EN			(1 << 5)
+#define CIRQ_EN			(1 << 8)
 #define ERR_EN			(1 << 15)
 #define CTO_EN			(1 << 16)
 #define CCRC_EN			(1 << 17)
@@ -210,6 +211,9 @@  struct omap_hsmmc_host {
 	int			reqs_blocked;
 	int			use_reg;
 	int			req_in_progress;
+	int                     flags;
+#define HSMMC_SDIO_IRQ_ENABLED	(1 << 0)        /* SDIO irq enabled */
+
 	struct omap_hsmmc_next	next_data;
 	struct	omap_mmc_platform_data	*pdata;
 };
@@ -490,27 +494,40 @@  static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
 static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
 				  struct mmc_command *cmd)
 {
-	unsigned int irq_mask;
+	u32 irq_mask = INT_EN_MASK;
+	unsigned long flags;
 
 	if (host->use_dma)
-		irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
-	else
-		irq_mask = INT_EN_MASK;
+		irq_mask &= ~(BRR_EN | BWR_EN);
 
 	/* Disable timeout for erases */
 	if (cmd->opcode == MMC_ERASE)
 		irq_mask &= ~DTO_EN;
 
+	spin_lock_irqsave(&host->irq_lock, flags);
 	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 	OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+
+	/* latch pending CIRQ, but don't signal */
+	if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
+		irq_mask |= CIRQ_EN;
 	OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
+	spin_unlock_irqrestore(&host->irq_lock, flags);
 }
 
 static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
 {
-	OMAP_HSMMC_WRITE(host->base, ISE, 0);
-	OMAP_HSMMC_WRITE(host->base, IE, 0);
+	u32 irq_mask = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->irq_lock, flags);
+	/* no transfer running, need to signal cirq if enabled */
+	if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
+		irq_mask |= CIRQ_EN;
+	OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+	OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
 	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+	spin_unlock_irqrestore(&host->irq_lock, flags);
 }
 
 /* Calculate divisor for the given clock frequency */
@@ -1067,8 +1084,12 @@  static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
 	int status;
 
 	status = OMAP_HSMMC_READ(host->base, STAT);
-	while (status & INT_EN_MASK && host->req_in_progress) {
-		omap_hsmmc_do_irq(host, status);
+	while (status & (INT_EN_MASK | CIRQ_EN)) {
+		if (host->req_in_progress)
+			omap_hsmmc_do_irq(host, status);
+
+		if (status & CIRQ_EN)
+			mmc_signal_sdio_irq(host->mmc);
 
 		/* Flush posted write */
 		status = OMAP_HSMMC_READ(host->base, STAT);
@@ -1583,6 +1604,37 @@  static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
 		mmc_slot(host).init_card(card);
 }
 
+static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct omap_hsmmc_host *host = mmc_priv(mmc);
+	u32 irq_mask;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->irq_lock, flags);
+
+	irq_mask = OMAP_HSMMC_READ(host->base, ISE);
+	if (enable) {
+		host->flags |= HSMMC_SDIO_IRQ_ENABLED;
+		irq_mask |= CIRQ_EN;
+	} else {
+		host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
+		irq_mask &= ~CIRQ_EN;
+	}
+	OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
+
+	/*
+	 * if enable, piggy back detection on current request
+	 * but always disable immediately
+	 */
+	if (!host->req_in_progress || !enable)
+		OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+
+	/* flush posted write */
+	OMAP_HSMMC_READ(host->base, IE);
+
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+}
+
 static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
 {
 	u32 hctl, capa, value;
@@ -1635,7 +1687,7 @@  static const struct mmc_host_ops omap_hsmmc_ops = {
 	.get_cd = omap_hsmmc_get_cd,
 	.get_ro = omap_hsmmc_get_ro,
 	.init_card = omap_hsmmc_init_card,
-	/* NYET -- enable_sdio_irq */
+	.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -2021,6 +2073,22 @@  static int omap_hsmmc_probe(struct platform_device *pdev)
 		dev_warn(&pdev->dev,
 			"pins are not configured from the driver\n");
 
+	/*
+	 * For now, only support SDIO interrupt if we are booted with
+	 * DT. This is because some platforms need special quirks. And
+	 * we don't want to add new legacy mux platform init code
+	 * callbacks any longer as we are moving to DT based booting
+	 * anyways.
+	 */
+	if (pdev->dev.of_node) {
+		mmc->caps |= MMC_CAP_SDIO_IRQ;
+		if (of_find_property(host->dev->of_node,
+				     "ti,quirk-swakeup-missing", NULL)) {
+			/* no wakeup from deeper power states, use polling */
+			mmc->caps &= ~MMC_CAP_SDIO_IRQ;
+		}
+	}
+
 	omap_hsmmc_protect_card(host);
 
 	mmc_add_host(mmc);