All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 resend 0/3] mmc: omap_hsmmc: Enable SDIO IRQ.
@ 2014-03-21 12:20 Andreas Fenkart
  2014-03-21 12:20 ` [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-21 12:20 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque,
	galak, linux-doc, linux-mmc, linux-omap, Andreas Fenkart

Resend of v8, with Chris Ball on cc and following changes.

v9
- extended comment about why wake-irq is needed
- drop double '(' ')' around card_detect_irq
- drop final '.' in in subject line of patch

v8
- rebased on top of Tony Lindgren<tony@atomide.com> changes
  - improved changelog describing the earlier work
  - improved wakeup irq setup
  - works for am3730 es platform now
- my changes on top:
  - compile tested with #undef CONFIG_OF
  - disable wake_irq in handler to prevent infinite loop  
  - fixed typo and added comment about wake-irq

v7
- rebase on 3.14.0-rc3-49726-g77e15ec
- split omap_hsmmc_pin_init due to regression on omap-3730 platform

v6
- rebase on Linux 3.13-rc3
- reformatting debugfs

v5
- fix compile error introduced by last minute one line fix

v4:
- switch to interrupts-extended format
- drop ti,swakeup-missing flag convert to comaptible section

v3:
- removed gpio_irq from platform_data

v2:
- incorparated changes as suggested by reviewers
- simplified workaround for am335x, gpio will now only wake
  the module from runtime suspend, not handle the sdio irq
  itself 

Andreas Fenkart (3):
  mmc: omap_hsmmc: Enable SDIO interrupt
  mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on
    AM335x
  mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux

 .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   50 +++
 drivers/mmc/host/omap_hsmmc.c                      |  339 ++++++++++++++++++--
 include/linux/platform_data/mmc-omap.h             |    1 +
 3 files changed, 365 insertions(+), 25 deletions(-)

-- 
1.7.10.4


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

* [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-21 12:20 [PATCH v9 resend 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
@ 2014-03-21 12:20 ` Andreas Fenkart
  2014-03-21 16:10   ` Balaji T K
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
  2014-03-21 12:20 ` [PATCH v9 resend 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
  2014-03-21 12:20 ` [PATCH v9 resend 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Andreas Fenkart
  2 siblings, 2 replies; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-21 12:20 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque,
	galak, linux-doc, linux-mmc, linux-omap, Andreas Fenkart

There have been various patches floating around for enabling
the SDIO IRQ for hsmmc, but none of them ever got merged.

Probably the reason for not merging the SDIO interrupt patches
has been the lack of wake-up path for SDIO on some omaps that
has also needed remuxing the SDIO DAT1 line to a GPIO making
the patches complex.

This patch adds the minimal SDIO IRQ support to hsmmc for
omaps that do have the wake-up path. For those omaps, the
DAT1 line need to have the wake-up enable bit set, and the
wake-up interrupt is the same as for the MMC controller.

This patch has been tested on am3730 es1.2 with mwifiex
connected to MMC3 with mwifiex waking to Ethernet traffic
from off-idle mode. Note that for omaps that do not have
the SDIO wake-up path, this patch will not work for idle
modes and further patches for remuxing DAT1 to GPIO are
needed.

Based on earlier patches [1][2] by David Vrabel
<david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
handing improved following how sdhci.c is doing it.

For now, only support SDIO interrupt if we are booted with
a separate wake-irq configued via device tree. This is
because omaps need the wake-irq for idle states, and some
omaps 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.

To use it, you need to specify the wake-irq using the
interrupts-extended property.

[1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
[2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446

Cc: Balaji T K <balajitk@ti.com>
Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 317a9d5..06bf669 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -29,6 +29,7 @@
 #include <linux/timer.h>
 #include <linux/clk.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
 #include <linux/omap-dma.h>
@@ -36,6 +37,7 @@
 #include <linux/mmc/core.h>
 #include <linux/mmc/mmc.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pinctrl/consumer.h>
@@ -131,6 +133,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)
@@ -205,6 +208,8 @@ struct omap_hsmmc_host {
 	u32			sysctl;
 	u32			capa;
 	int			irq;
+	int			wake_irq;
+	int			wake_irq_en;
 	int			use_dma, dma_ch;
 	struct dma_chan		*tx_chan;
 	struct dma_chan		*rx_chan;
@@ -215,6 +220,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 */
+#define HSMMC_SWAKEUP_QUIRK	(1 << 1)
 	struct omap_hsmmc_next	next_data;
 	struct	omap_mmc_platform_data	*pdata;
 };
@@ -495,27 +503,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 MMC core */
+	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 but need to keep 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 */
@@ -1078,8 +1099,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);
@@ -1088,6 +1113,45 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static inline void hsmmc_enable_wake_irq(struct omap_hsmmc_host *host)
+{
+	unsigned long flags;
+
+	if (!host->wake_irq)
+		return;
+
+	spin_lock_irqsave(&host->irq_lock, flags);
+	enable_irq(host->wake_irq);
+	host->wake_irq_en = true;
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+}
+
+static inline void hsmmc_disable_wake_irq(struct omap_hsmmc_host *host)
+{
+	unsigned long flags;
+
+	if (!host->wake_irq)
+		return;
+
+	spin_lock_irqsave(&host->irq_lock, flags);
+	if (host->wake_irq_en)
+		disable_irq_nosync(host->wake_irq);
+	host->wake_irq_en = false;
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+}
+
+static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
+{
+	struct omap_hsmmc_host *host = dev_id;
+
+	/* cirq is level triggered, disable to avoid infinite loop */
+	hsmmc_disable_wake_irq(host);
+
+	pm_request_resume(host->dev); /* no use counter */
+
+	return IRQ_HANDLED;
+}
+
 static void set_sd_bus_power(struct omap_hsmmc_host *host)
 {
 	unsigned long i;
@@ -1591,6 +1655,72 @@ 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 int omap_hscmm_configure_wake_irq(struct omap_hsmmc_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	int ret;
+
+	/*
+	 * The wake-irq is needed for omaps with wake-up path and also
+	 * when doing GPIO remuxing, because omap_hsmmc is doing runtime PM.
+	 * So there's nothing stopping from shutting it down. And there's
+	 * really no need to block runtime PM for it as it's working.
+	 */
+	if (!host->dev->of_node || !host->wake_irq)
+		return -ENODEV;
+
+	/* Prevent auto-enabling of IRQ */
+	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
+	ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
+			  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+			  mmc_hostname(mmc), host);
+	if (ret) {
+		dev_err(mmc_dev(host->mmc),
+			"Unable to request wake IRQ\n");
+		return ret;
+	}
+
+	/*
+	 * Some omaps don't have wake-up path from deeper idle states
+	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
+	 */
+	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
+		host->flags |= HSMMC_SWAKEUP_QUIRK;
+
+	return 0;
+}
+
 static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
 {
 	u32 hctl, capa, value;
@@ -1643,7 +1773,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
@@ -1704,8 +1834,19 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
 
 #endif
 
+struct of_data {
+	u16 offset;
+	int flags;
+};
+
 #ifdef CONFIG_OF
-static u16 omap4_reg_offset = 0x100;
+static struct of_data omap4_data = {
+	.offset	= 0x100,
+};
+static struct of_data am33xx_data = {
+	.offset	= 0x100,
+	.flags	= OMAP_HSMMC_SWAKEUP_MISSING,
+};
 
 static const struct of_device_id omap_mmc_of_match[] = {
 	{
@@ -1716,7 +1857,11 @@ static const struct of_device_id omap_mmc_of_match[] = {
 	},
 	{
 		.compatible = "ti,omap4-hsmmc",
-		.data = &omap4_reg_offset,
+		.data = &omap4_data,
+	},
+	{
+		.compatible = "ti,am33xx-hsmmc",
+		.data = &am33xx_data,
 	},
 	{},
 };
@@ -1779,6 +1924,7 @@ static inline struct omap_mmc_platform_data
 {
 	return NULL;
 }
+#define omap_mmc_of_match	NULL
 #endif
 
 static int omap_hsmmc_probe(struct platform_device *pdev)
@@ -1787,7 +1933,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	struct mmc_host *mmc;
 	struct omap_hsmmc_host *host = NULL;
 	struct resource *res;
-	int ret, irq;
+	int ret, irq, _wake_irq = 0;
 	const struct of_device_id *match;
 	dma_cap_mask_t mask;
 	unsigned tx_req, rx_req;
@@ -1801,8 +1947,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 			return PTR_ERR(pdata);
 
 		if (match->data) {
-			const u16 *offsetp = match->data;
-			pdata->reg_offset = *offsetp;
+			const struct of_data *d = match->data;
+			pdata->reg_offset = d->offset;
+			pdata->controller_flags |= d->flags;
 		}
 	}
 
@@ -1821,6 +1968,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	if (res == NULL || irq < 0)
 		return -ENXIO;
 
+	if (pdev->dev.of_node)
+		_wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
+
 	res = request_mem_region(res->start, resource_size(res), pdev->name);
 	if (res == NULL)
 		return -EBUSY;
@@ -1842,6 +1992,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	host->use_dma	= 1;
 	host->dma_ch	= -1;
 	host->irq	= irq;
+	host->wake_irq = _wake_irq;
 	host->slot_id	= 0;
 	host->mapbase	= res->start + pdata->reg_offset;
 	host->base	= ioremap(host->mapbase, SZ_4K);
@@ -2018,6 +2169,18 @@ 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 have a separate
+	 * wake-up interrupt configured from device tree. This is because
+	 * the wake-up interrupt is needed for idle state and 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.
+	 */
+	ret = omap_hscmm_configure_wake_irq(host);
+	if (!ret)
+		mmc->caps |= MMC_CAP_SDIO_IRQ;
+
 	omap_hsmmc_protect_card(host);
 
 	mmc_add_host(mmc);
@@ -2042,7 +2205,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 err_slot_name:
 	mmc_remove_host(mmc);
-	free_irq(mmc_slot(host).card_detect_irq, host);
+	if (host->wake_irq)
+		free_irq(host->wake_irq, host);
+	if (mmc_slot(host).card_detect_irq)
+		free_irq(mmc_slot(host).card_detect_irq, host);
 err_irq_cd:
 	if (host->use_reg)
 		omap_hsmmc_reg_put(host);
@@ -2087,6 +2253,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 	if (host->pdata->cleanup)
 		host->pdata->cleanup(&pdev->dev);
 	free_irq(host->irq, host);
+	if (host->wake_irq)
+		free_irq(host->wake_irq, host);
 	if (mmc_slot(host).card_detect_irq)
 		free_irq(mmc_slot(host).card_detect_irq, host);
 
@@ -2149,6 +2317,8 @@ static int omap_hsmmc_suspend(struct device *dev)
 				OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
 	}
 
+	hsmmc_disable_wake_irq(host);
+
 	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
 
@@ -2176,6 +2346,8 @@ static int omap_hsmmc_resume(struct device *dev)
 
 	omap_hsmmc_protect_card(host);
 
+	hsmmc_enable_wake_irq(host);
+
 	pm_runtime_mark_last_busy(host->dev);
 	pm_runtime_put_autosuspend(host->dev);
 	return 0;
@@ -2191,23 +2363,38 @@ static int omap_hsmmc_resume(struct device *dev)
 static int omap_hsmmc_runtime_suspend(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
+	int ret = 0;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_save(host);
 	dev_dbg(dev, "disabled\n");
 
-	return 0;
+	if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+		OMAP_HSMMC_WRITE(host->base, ISE, 0);
+		OMAP_HSMMC_WRITE(host->base, IE, 0);
+		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+		hsmmc_enable_wake_irq(host);
+	}
+
+	return ret;
 }
 
 static int omap_hsmmc_runtime_resume(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
+	int ret = 0;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_restore(host);
 	dev_dbg(dev, "enabled\n");
 
-	return 0;
+	if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+		hsmmc_disable_wake_irq(host);
+		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+		OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
+		OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
+	}
+	return ret;
 }
 
 static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
index 2bf1b30..51e70cf 100644
--- a/include/linux/platform_data/mmc-omap.h
+++ b/include/linux/platform_data/mmc-omap.h
@@ -28,6 +28,7 @@
  */
 #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT		BIT(0)
 #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ	BIT(1)
+#define OMAP_HSMMC_SWAKEUP_MISSING		BIT(2)
 
 struct mmc_card;
 
-- 
1.7.10.4


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

* [PATCH v9 resend 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x
  2014-03-21 12:20 [PATCH v9 resend 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
  2014-03-21 12:20 ` [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
@ 2014-03-21 12:20 ` Andreas Fenkart
  2014-03-21 12:20 ` [PATCH v9 resend 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Andreas Fenkart
  2 siblings, 0 replies; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-21 12:20 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque,
	galak, linux-doc, linux-mmc, linux-omap, Andreas Fenkart

The am335x can't detect pending cirq in PM runtime suspend.
This patch reconfigures dat1 as a GPIO before going to suspend.
SDIO interrupts are detected with the GPIO, the GPIO will only wake
the module from suspend, SDIO irq detection will still happen through the
IP block.

Idea of remuxing the pins and some minor changes by Tony Lindgren.

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index 8c8908a..8e1195e 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -55,3 +55,53 @@ Examples:
 			&edma 25>;
 		dma-names = "tx", "rx";
 	};
+
+[workaround for missing swakeup on am33xx]
+
+This SOC is missing the swakeup line, it will not detect SDIO irq
+while in suspend.
+
+                             ------
+                             | 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. Since am33xx
+doesn't have this line it never wakes from suspend.
+
+The workaround is to reconfigure the dat1 line as a GPIO upon
+suspend. To make this work, we need to set 1) the named pinctrl
+states "default", "active" and "idle", 2) the gpio detecting
+sdio irq in suspend and 3) compatibe section, see example below.
+The MMC driver will then toggle between active and idle during
+the runtime. If configuration is incomplete, a warning message is
+emitted "falling back to polling".  Mind not every application
+needs SDIO irq, e.g. MMC cards Affected chips are am335x,
+probably others
+
+
+	mmc1: mmc@48060100 {
+		compatible = "ti,am33xx-hsmmc";
+		...
+		interrupts-extended = <&intc 64 &gpio2 28 0>;
+		...
+		pinctrl-names = "default", "active", "idle"
+		pinctrl-0 = <&mmc1_pins>;
+		pinctrl-1 = <&mmc1_pins>;
+		pinctrl-2 = <&mmc1_cirq_pin>;
+		...
+	};
+
+	mmc1_cirq_pin: pinmux_cirq_pin {
+		pinctrl-single,pins = <
+		        0x0f8 0x3f      /* GPIO2_28 */
+		>;
+	};
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 06bf669..2307c78 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -224,6 +224,8 @@ struct omap_hsmmc_host {
 #define HSMMC_SDIO_IRQ_ENABLED	(1 << 0)        /* SDIO irq enabled */
 #define HSMMC_SWAKEUP_QUIRK	(1 << 1)
 	struct omap_hsmmc_next	next_data;
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*fixed, *active, *idle;
 	struct	omap_mmc_platform_data	*pdata;
 };
 
@@ -480,6 +482,70 @@ static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
 		gpio_free(pdata->slots[0].switch_pin);
 }
 
+static int omap_hsmmc_pin_init(struct omap_hsmmc_host *host)
+{
+	struct pinctrl *_pinctrl;
+	int ret;
+
+	_pinctrl = devm_pinctrl_get(host->dev);
+	if (IS_ERR(_pinctrl)) {
+		/* okay, if the bootloader set it up right */
+		dev_warn(host->dev, "no pinctrl handle\n");
+		return 0;
+	}
+
+	host->fixed = pinctrl_lookup_state(_pinctrl,
+					   PINCTRL_STATE_DEFAULT);
+	if (IS_ERR(host->fixed)) {
+		dev_info(host->dev,
+			 "pins are not configured from the driver\n");
+		host->fixed = NULL;
+		devm_pinctrl_put(_pinctrl);
+		return 0;
+	}
+
+	ret = pinctrl_select_state(_pinctrl, host->fixed);
+	if (ret < 0) {
+		dev_warn(host->dev, "fixed pinctrl state failed %d\n", ret);
+		goto err;
+	}
+
+	/* For most cases we don't have wake-ups, and exit after this */
+	host->active = pinctrl_lookup_state(_pinctrl, "active");
+	if (IS_ERR(host->active)) {
+		ret = PTR_ERR(host->active);
+		host->active = NULL;
+		goto done;
+	}
+
+	host->idle = pinctrl_lookup_state(_pinctrl, PINCTRL_STATE_IDLE);
+	if (IS_ERR(host->idle)) {
+		ret = PTR_ERR(host->idle);
+		host->idle = NULL;
+		goto err;
+	}
+
+	/* Let's make sure the active and idle states work */
+	ret = pinctrl_select_state(_pinctrl, host->idle);
+	if (ret < 0)
+		goto err;
+
+	ret = pinctrl_select_state(_pinctrl, host->active);
+	if (ret < 0)
+		goto err;
+
+	dev_info(mmc_dev(host->mmc), "pins configured for wake-up events\n");
+
+done:
+	host->pinctrl = _pinctrl;
+	return 0;
+
+err:
+	dev_err(mmc_dev(host->mmc), "pins configuration error: %i\n", ret);
+	devm_pinctrl_put(_pinctrl);
+	return ret;
+}
+
 /*
  * Start clock to the card
  */
@@ -1715,8 +1781,14 @@ static int omap_hscmm_configure_wake_irq(struct omap_hsmmc_host *host)
 	 * Some omaps don't have wake-up path from deeper idle states
 	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
 	 */
-	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
+	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
+		if (!host->idle) {
+			free_irq(host->wake_irq, host);
+			host->wake_irq = 0;
+			return -EINVAL;
+		}
 		host->flags |= HSMMC_SWAKEUP_QUIRK;
+	}
 
 	return 0;
 }
@@ -1937,7 +2009,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	dma_cap_mask_t mask;
 	unsigned tx_req, rx_req;
-	struct pinctrl *pinctrl;
 
 	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
 	if (match) {
@@ -2164,10 +2235,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	omap_hsmmc_disable_irq(host);
 
-	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-	if (IS_ERR(pinctrl))
-		dev_warn(&pdev->dev,
-			"pins are not configured from the driver\n");
+	ret = omap_hsmmc_pin_init(host);
+	if (ret)
+		goto err_pinctrl_state;
 
 	/*
 	 * For now, only support SDIO interrupt if we have a separate
@@ -2207,6 +2277,9 @@ err_slot_name:
 	mmc_remove_host(mmc);
 	if (host->wake_irq)
 		free_irq(host->wake_irq, host);
+	if (host->pinctrl)
+		devm_pinctrl_put(host->pinctrl);
+err_pinctrl_state:
 	if (mmc_slot(host).card_detect_irq)
 		free_irq(mmc_slot(host).card_detect_irq, host);
 err_irq_cd:
@@ -2262,6 +2335,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 		dma_release_channel(host->tx_chan);
 	if (host->rx_chan)
 		dma_release_channel(host->rx_chan);
+	if (host->pinctrl)
+		devm_pinctrl_put(host->pinctrl);
 
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
@@ -2373,6 +2448,11 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 		OMAP_HSMMC_WRITE(host->base, ISE, 0);
 		OMAP_HSMMC_WRITE(host->base, IE, 0);
 		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+		if (host->idle) {
+			ret = pinctrl_select_state(host->pinctrl, host->idle);
+			if (ret < 0)
+				dev_warn(mmc_dev(host->mmc), "Unable to select idle pinmux\n");
+		}
 		hsmmc_enable_wake_irq(host);
 	}
 
@@ -2390,6 +2470,11 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
 
 	if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
 		hsmmc_disable_wake_irq(host);
+		if (host->active) {
+			ret = pinctrl_select_state(host->pinctrl, host->active);
+			if (ret < 0)
+				dev_warn(mmc_dev(host->mmc), "Unable to select active pinmux\n");
+		}
 		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 		OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
 		OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
-- 
1.7.10.4


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

* [PATCH v9 resend 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux
  2014-03-21 12:20 [PATCH v9 resend 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
  2014-03-21 12:20 ` [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
  2014-03-21 12:20 ` [PATCH v9 resend 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
@ 2014-03-21 12:20 ` Andreas Fenkart
  2 siblings, 0 replies; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-21 12:20 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque,
	galak, linux-doc, linux-mmc, linux-omap, Andreas Fenkart

Add SDIO IRQ entries to debugfs entry. Note that PSTATE shows current
state of data lines, incl. SDIO IRQ pending

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 2307c78..681bf68 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -82,6 +82,7 @@ static void apply_clk_hack(struct device *dev)
 #define OMAP_HSMMC_RSP54	0x0118
 #define OMAP_HSMMC_RSP76	0x011C
 #define OMAP_HSMMC_DATA		0x0120
+#define OMAP_HSMMC_PSTATE	0x0124
 #define OMAP_HSMMC_HCTL		0x0128
 #define OMAP_HSMMC_SYSCTL	0x012C
 #define OMAP_HSMMC_STAT		0x0130
@@ -1854,14 +1855,30 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
 {
 	struct mmc_host *mmc = s->private;
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
+	const char *cirq_state;
+	bool suspended;
 
-	seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
-			mmc->index, host->context_loss);
+	seq_printf(s, "mmc%d:\n", mmc->index);
+	if (mmc->caps & MMC_CAP_SDIO_IRQ)
+		cirq_state = (host->flags & HSMMC_SDIO_IRQ_ENABLED) ?
+			"enabled" : "disabled";
+	else
+		cirq_state = "polling";
+	seq_printf(s, "sdio irq\t%s\n", cirq_state);
 
-	pm_runtime_get_sync(host->dev);
+	if (host->flags & HSMMC_SWAKEUP_QUIRK) {
+		suspended = host->dev->power.runtime_status != RPM_ACTIVE;
+		seq_printf(s, "pinmux config\t%s\n", (suspended ?
+						      "gpio" : "sdio"));
+	}
+	seq_printf(s, "ctx_loss:\t%d\n", host->context_loss);
 
+	pm_runtime_get_sync(host->dev);
+	seq_puts(s, "\nregs:\n");
 	seq_printf(s, "CON:\t\t0x%08x\n",
 			OMAP_HSMMC_READ(host->base, CON));
+	seq_printf(s, "PSTATE:\t\t0x%08x\n",
+		   OMAP_HSMMC_READ(host->base, PSTATE));
 	seq_printf(s, "HCTL:\t\t0x%08x\n",
 			OMAP_HSMMC_READ(host->base, HCTL));
 	seq_printf(s, "SYSCTL:\t\t0x%08x\n",
-- 
1.7.10.4


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

* Re: [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-21 12:20 ` [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
@ 2014-03-21 16:10   ` Balaji T K
  2014-03-24  9:30     ` Dmitry Lifshitz
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
  1 sibling, 1 reply; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:10 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi, zonque,
	galak, linux-doc, linux-mmc, linux-omap

On Friday 21 March 2014 05:50 PM, Andreas Fenkart wrote:

Thanks Andreas for the patch series

I rebased against latest mmc-next, made few changes to your patch.
I have hosted your series along with devm cleanups on a branch[1] for testing
[1]
git://git.ti.com/~balajitk/ti-linux-kernel/omap-hsmmc.git omap_hsmmc_sdio_irq_devm_cleanup

Can you please test on your platform and provide feedback.

Details about the changes below.

> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> @@ -1088,6 +1113,45 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>   	return IRQ_HANDLED;
>   }
>
> +static inline void hsmmc_enable_wake_irq(struct omap_hsmmc_host *host)
> +{
> +	unsigned long flags;
> +
> +	if (!host->wake_irq)
> +		return;
> +
> +	spin_lock_irqsave(&host->irq_lock, flags);
> +	enable_irq(host->wake_irq);
> +	host->wake_irq_en = true;

Using wake_irq_en flag leads to wake_irq enabled always after
suspend/resume due to unbalanced disable/enable_irq
so adding back HSMMC_WAKE_IRQ_ENABLED to host->flags

> +	spin_unlock_irqrestore(&host->irq_lock, flags);
> +}
> +
> +static inline void hsmmc_disable_wake_irq(struct omap_hsmmc_host *host)
> +{
> +	unsigned long flags;
> +
> +	if (!host->wake_irq)
> +		return;
> +
> +	spin_lock_irqsave(&host->irq_lock, flags);
> +	if (host->wake_irq_en)
> +		disable_irq_nosync(host->wake_irq);
> +	host->wake_irq_en = false;
> +	spin_unlock_irqrestore(&host->irq_lock, flags);
> +}
> +
> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
> +{
> +	struct omap_hsmmc_host *host = dev_id;
> +
> +	/* cirq is level triggered, disable to avoid infinite loop */
> +	hsmmc_disable_wake_irq(host);
> +
> +	pm_request_resume(host->dev); /* no use counter */
> +
> +	return IRQ_HANDLED;
> +}
> +
>   static void set_sd_bus_power(struct omap_hsmmc_host *host)
>   {
>   	unsigned long i;
> @@ -1591,6 +1655,72 @@ 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);
> +

Introduced check for runtime suspend to be sure and explicitly
enable clocks using runtime_get_sync for enable sdio irq path.

> +	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 int omap_hscmm_configure_wake_irq(struct omap_hsmmc_host *host)
> +{
> +	struct mmc_host *mmc = host->mmc;
> +	int ret;
> +
> +	/*
> +	 * The wake-irq is needed for omaps with wake-up path and also
> +	 * when doing GPIO remuxing, because omap_hsmmc is doing runtime PM.
> +	 * So there's nothing stopping from shutting it down. And there's
> +	 * really no need to block runtime PM for it as it's working.
> +	 */
> +	if (!host->dev->of_node || !host->wake_irq)
> +		return -ENODEV;
> +
> +	/* Prevent auto-enabling of IRQ */
> +	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
> +	ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
> +			  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> +			  mmc_hostname(mmc), host);

Replaced request_irq with devm_request_irq

> +	if (ret) {
> +		dev_err(mmc_dev(host->mmc),
> +			"Unable to request wake IRQ\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Some omaps don't have wake-up path from deeper idle states
> +	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
> +	 */
> +	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
> +		host->flags |= HSMMC_SWAKEUP_QUIRK;
> +
> +	return 0;
> +}
> +
>   static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>   {
>   	u32 hctl, capa, value;
> @@ -1643,7 +1773,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
> @@ -1704,8 +1834,19 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
>
>   #endif
>
> +struct of_data {
> +	u16 offset;
> +	int flags;
> +};
> +
>   #ifdef CONFIG_OF
> -static u16 omap4_reg_offset = 0x100;
> +static struct of_data omap4_data = {
> +	.offset	= 0x100,
> +};
> +static struct of_data am33xx_data = {
> +	.offset	= 0x100,
> +	.flags	= OMAP_HSMMC_SWAKEUP_MISSING,
> +};
>
>   static const struct of_device_id omap_mmc_of_match[] = {
>   	{
> @@ -1716,7 +1857,11 @@ static const struct of_device_id omap_mmc_of_match[] = {
>   	},
>   	{
>   		.compatible = "ti,omap4-hsmmc",
> -		.data = &omap4_reg_offset,
> +		.data = &omap4_data,
> +	},
> +	{
> +		.compatible = "ti,am33xx-hsmmc",
> +		.data = &am33xx_data,
>   	},
>   	{},
>   };
> @@ -1779,6 +1924,7 @@ static inline struct omap_mmc_platform_data
>   {
>   	return NULL;
>   }
> +#define omap_mmc_of_match	NULL
>   #endif
>
>   static int omap_hsmmc_probe(struct platform_device *pdev)
> @@ -1787,7 +1933,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>   	struct mmc_host *mmc;
>   	struct omap_hsmmc_host *host = NULL;
>   	struct resource *res;
> -	int ret, irq;
> +	int ret, irq, _wake_irq = 0;
>   	const struct of_device_id *match;
>   	dma_cap_mask_t mask;
>   	unsigned tx_req, rx_req;
> @@ -1801,8 +1947,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>   			return PTR_ERR(pdata);
>
>   		if (match->data) {
> -			const u16 *offsetp = match->data;
> -			pdata->reg_offset = *offsetp;
> +			const struct of_data *d = match->data;
> +			pdata->reg_offset = d->offset;
> +			pdata->controller_flags |= d->flags;
>   		}
>   	}
>
> @@ -1821,6 +1968,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>   	if (res == NULL || irq < 0)
>   		return -ENXIO;
>
> +	if (pdev->dev.of_node)
> +		_wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
> +

Moved this code further down to remove _wake_irq

>   	res = request_mem_region(res->start, resource_size(res), pdev->name);
>   	if (res == NULL)
>   		return -EBUSY;
> @@ -1842,6 +1992,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>   	host->use_dma	= 1;
>   	host->dma_ch	= -1;
>   	host->irq	= irq;
> +	host->wake_irq = _wake_irq;
>   	host->slot_id	= 0;
>   	host->mapbase	= res->start + pdata->reg_offset;
>   	host->base	= ioremap(host->mapbase, SZ_4K);
> @@ -2018,6 +2169,18 @@ 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 have a separate
> +	 * wake-up interrupt configured from device tree. This is because
> +	 * the wake-up interrupt is needed for idle state and 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.
> +	 */
> +	ret = omap_hscmm_configure_wake_irq(host);

fixed the typo in hsmmc :-)

> +	if (!ret)
> +		mmc->caps |= MMC_CAP_SDIO_IRQ;
> +
>   	omap_hsmmc_protect_card(host);
>
>   	mmc_add_host(mmc);
> @@ -2042,7 +2205,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>
>   err_slot_name:
>   	mmc_remove_host(mmc);
> -	free_irq(mmc_slot(host).card_detect_irq, host);
> +	if (host->wake_irq)
> +		free_irq(host->wake_irq, host);
> +	if (mmc_slot(host).card_detect_irq)
> +		free_irq(mmc_slot(host).card_detect_irq, host);
>   err_irq_cd:
>   	if (host->use_reg)
>   		omap_hsmmc_reg_put(host);
> @@ -2087,6 +2253,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
>   	if (host->pdata->cleanup)
>   		host->pdata->cleanup(&pdev->dev);
>   	free_irq(host->irq, host);
> +	if (host->wake_irq)
> +		free_irq(host->wake_irq, host);
>   	if (mmc_slot(host).card_detect_irq)
>   		free_irq(mmc_slot(host).card_detect_irq, host);
>
> @@ -2149,6 +2317,8 @@ static int omap_hsmmc_suspend(struct device *dev)
>   				OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>   	}
>
> +	hsmmc_disable_wake_irq(host);
> +

Made disable/enable_wake_irq conditional on MMC_PM_WAKE_SDIO_IRQ cap

>   	if (host->dbclk)
>   		clk_disable_unprepare(host->dbclk);
>
> @@ -2176,6 +2346,8 @@ static int omap_hsmmc_resume(struct device *dev)
>
>   	omap_hsmmc_protect_card(host);
>
> +	hsmmc_enable_wake_irq(host);
> +
>   	pm_runtime_mark_last_busy(host->dev);
>   	pm_runtime_put_autosuspend(host->dev);
>   	return 0;
> @@ -2191,23 +2363,38 @@ static int omap_hsmmc_resume(struct device *dev)
>   static int omap_hsmmc_runtime_suspend(struct device *dev)
>   {
>   	struct omap_hsmmc_host *host;
> +	int ret = 0;
>
>   	host = platform_get_drvdata(to_platform_device(dev));
>   	omap_hsmmc_context_save(host);
>   	dev_dbg(dev, "disabled\n");
>
> -	return 0;
> +	if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
> +		OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +		OMAP_HSMMC_WRITE(host->base, IE, 0);
> +		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +		hsmmc_enable_wake_irq(host);

here too.

> +	}
> +
> +	return ret;
>   }
>
>   static int omap_hsmmc_runtime_resume(struct device *dev)
>   {
>   	struct omap_hsmmc_host *host;
> +	int ret = 0;
>
>   	host = platform_get_drvdata(to_platform_device(dev));
>   	omap_hsmmc_context_restore(host);
>   	dev_dbg(dev, "enabled\n");
>
> -	return 0;
> +	if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {

This leads to unconditional re-enabling sdio_irq/wake_irq
on next runtime resume, so replaced the check with HSMMC_SDIO_IRQ_ENABLED

> +		hsmmc_disable_wake_irq(host);
> +		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +		OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
> +		OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
> +	}
> +	return ret;
>   }
>
>   static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
> diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
> index 2bf1b30..51e70cf 100644
> --- a/include/linux/platform_data/mmc-omap.h
> +++ b/include/linux/platform_data/mmc-omap.h
> @@ -28,6 +28,7 @@
>    */
>   #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT		BIT(0)
>   #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ	BIT(1)
> +#define OMAP_HSMMC_SWAKEUP_MISSING		BIT(2)
>
>   struct mmc_card;
>
>


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

* [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq
  2014-03-21 12:20 ` [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
  2014-03-21 16:10   ` Balaji T K
@ 2014-03-21 16:17   ` Balaji T K
  2014-03-21 16:17     ` [PATCH 1/9] mmc: omap_hsmmc: use devm_clk_get Balaji T K
                       ` (8 more replies)
  1 sibling, 9 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

Andreas Fenkart (3):
  mmc: omap_hsmmc: Enable SDIO interrupt
  mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on
    AM335x
  mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux

Balaji T K (6):
  mmc: omap_hsmmc: use devm_clk_get
  mmc: omap_hsmmc: use devm_request_irq
  mmc: omap_hsmmc: use devm_request_threaded_irq
  mmc: omap_hsmmc: use devm_request_mem_region
  mmc: omap_hsmmc: use devm_ioremap
  mmc: omap_hsmmc: enable wakeup event for sdio

 .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   50 +++
 drivers/mmc/host/omap_hsmmc.c                      |  349 +++++++++++++++++---
 include/linux/platform_data/mmc-omap.h             |    1 +
 3 files changed, 346 insertions(+), 54 deletions(-)

-- 
1.7.5.4


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

* [PATCH 1/9] mmc: omap_hsmmc: use devm_clk_get
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:17     ` [PATCH 2/9] mmc: omap_hsmmc: use devm_request_irq Balaji T K
                       ` (7 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

With devm_clk_get conversion clk_put can be removed in clean up path

Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |   15 ++++-----------
 1 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e91ee21..578e983 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1922,7 +1922,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	spin_lock_init(&host->irq_lock);
 
-	host->fclk = clk_get(&pdev->dev, "fck");
+	host->fclk = devm_clk_get(&pdev->dev, "fck");
 	if (IS_ERR(host->fclk)) {
 		ret = PTR_ERR(host->fclk);
 		host->fclk = NULL;
@@ -1941,7 +1941,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	omap_hsmmc_context_save(host);
 
-	host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
+	host->dbclk = devm_clk_get(&pdev->dev, "mmchsdb_fck");
 	/*
 	 * MMC can still work without debounce clock.
 	 */
@@ -1949,7 +1949,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 		host->dbclk = NULL;
 	} else if (clk_prepare_enable(host->dbclk) != 0) {
 		dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
-		clk_put(host->dbclk);
 		host->dbclk = NULL;
 	}
 
@@ -2105,11 +2104,8 @@ err_irq:
 		dma_release_channel(host->rx_chan);
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
-	clk_put(host->fclk);
-	if (host->dbclk) {
+	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
-		clk_put(host->dbclk);
-	}
 err1:
 	iounmap(host->base);
 	mmc_free_host(mmc);
@@ -2144,11 +2140,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
-	clk_put(host->fclk);
-	if (host->dbclk) {
+	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
-		clk_put(host->dbclk);
-	}
 
 	omap_hsmmc_gpio_free(host->pdata);
 	iounmap(host->base);
-- 
1.7.5.4


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

* [PATCH 2/9] mmc: omap_hsmmc: use devm_request_irq
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
  2014-03-21 16:17     ` [PATCH 1/9] mmc: omap_hsmmc: use devm_clk_get Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:17     ` [PATCH 3/9] mmc: omap_hsmmc: use devm_request_threaded_irq Balaji T K
                       ` (6 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

With devm_request_irq conversion free_irq can be removed in clean up path

Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |    7 ++-----
 1 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 578e983..4a3bb4b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2017,7 +2017,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	}
 
 	/* Request IRQ for MMC operations */
-	ret = request_irq(host->irq, omap_hsmmc_irq, 0,
+	ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
 			mmc_hostname(mmc), host);
 	if (ret) {
 		dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
@@ -2028,7 +2028,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 		if (pdata->init(&pdev->dev) != 0) {
 			dev_err(mmc_dev(host->mmc),
 				"Unable to configure MMC IRQs\n");
-			goto err_irq_cd_init;
+			goto err_irq;
 		}
 	}
 
@@ -2095,8 +2095,6 @@ err_irq_cd:
 err_reg:
 	if (host->pdata->cleanup)
 		host->pdata->cleanup(&pdev->dev);
-err_irq_cd_init:
-	free_irq(host->irq, host);
 err_irq:
 	if (host->tx_chan)
 		dma_release_channel(host->tx_chan);
@@ -2129,7 +2127,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 		omap_hsmmc_reg_put(host);
 	if (host->pdata->cleanup)
 		host->pdata->cleanup(&pdev->dev);
-	free_irq(host->irq, host);
 	if (mmc_slot(host).card_detect_irq)
 		free_irq(mmc_slot(host).card_detect_irq, host);
 
-- 
1.7.5.4


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

* [PATCH 3/9] mmc: omap_hsmmc: use devm_request_threaded_irq
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
  2014-03-21 16:17     ` [PATCH 1/9] mmc: omap_hsmmc: use devm_clk_get Balaji T K
  2014-03-21 16:17     ` [PATCH 2/9] mmc: omap_hsmmc: use devm_request_irq Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:17     ` [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region Balaji T K
                       ` (5 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

With devm_request_threaded_irq conversion free_irq can be removed
in clean up path

Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |    9 +++------
 1 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 4a3bb4b..8e35a6e 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2043,9 +2043,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	/* Request IRQ for card detect */
 	if ((mmc_slot(host).card_detect_irq)) {
-		ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
-					   NULL,
-					   omap_hsmmc_detect,
+		ret = devm_request_threaded_irq(&pdev->dev,
+						mmc_slot(host).card_detect_irq,
+						NULL, omap_hsmmc_detect,
 					   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 					   mmc_hostname(mmc), host);
 		if (ret) {
@@ -2088,7 +2088,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 err_slot_name:
 	mmc_remove_host(mmc);
-	free_irq(mmc_slot(host).card_detect_irq, host);
 err_irq_cd:
 	if (host->use_reg)
 		omap_hsmmc_reg_put(host);
@@ -2127,8 +2126,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 		omap_hsmmc_reg_put(host);
 	if (host->pdata->cleanup)
 		host->pdata->cleanup(&pdev->dev);
-	if (mmc_slot(host).card_detect_irq)
-		free_irq(mmc_slot(host).card_detect_irq, host);
 
 	if (host->tx_chan)
 		dma_release_channel(host->tx_chan);
-- 
1.7.5.4


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

* [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
                       ` (2 preceding siblings ...)
  2014-03-21 16:17     ` [PATCH 3/9] mmc: omap_hsmmc: use devm_request_threaded_irq Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:18       ` Felipe Balbi
  2014-03-21 16:17     ` [PATCH 5/9] mmc: omap_hsmmc: use devm_ioremap Balaji T K
                       ` (4 subsequent siblings)
  8 siblings, 1 reply; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

With devm_request_mem_region conversion release_mem_region can be
removed in clean up path

Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |   11 ++---------
 1 files changed, 2 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 8e35a6e..9952673 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1881,7 +1881,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	if (res == NULL || irq < 0)
 		return -ENXIO;
 
-	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	res = devm_request_mem_region(&pdev->dev, res->start,
+					resource_size(res), pdev->name);
 	if (res == NULL)
 		return -EBUSY;
 
@@ -2109,16 +2110,12 @@ err1:
 err_alloc:
 	omap_hsmmc_gpio_free(pdata);
 err:
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (res)
-		release_mem_region(res->start, resource_size(res));
 	return ret;
 }
 
 static int omap_hsmmc_remove(struct platform_device *pdev)
 {
 	struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
-	struct resource *res;
 
 	pm_runtime_get_sync(host->dev);
 	mmc_remove_host(host->mmc);
@@ -2141,10 +2138,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 	iounmap(host->base);
 	mmc_free_host(host->mmc);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (res)
-		release_mem_region(res->start, resource_size(res));
-
 	return 0;
 }
 
-- 
1.7.5.4


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

* [PATCH 5/9] mmc: omap_hsmmc: use devm_ioremap
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
                       ` (3 preceding siblings ...)
  2014-03-21 16:17     ` [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:17     ` [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt Balaji T K
                       ` (3 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

With devm_ioremap conversion iounmap can be removed in clean up path

Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 9952673..38a75bc 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1905,7 +1905,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	host->irq	= irq;
 	host->slot_id	= 0;
 	host->mapbase	= res->start + pdata->reg_offset;
-	host->base	= ioremap(host->mapbase, SZ_4K);
+	host->base	= devm_ioremap(&pdev->dev, host->mapbase, SZ_4K);
 	host->power_mode = MMC_POWER_OFF;
 	host->next_data.cookie = 1;
 	host->pbias_enabled = 0;
@@ -2105,7 +2105,6 @@ err_irq:
 	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
 err1:
-	iounmap(host->base);
 	mmc_free_host(mmc);
 err_alloc:
 	omap_hsmmc_gpio_free(pdata);
@@ -2135,7 +2134,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 		clk_disable_unprepare(host->dbclk);
 
 	omap_hsmmc_gpio_free(host->pdata);
-	iounmap(host->base);
 	mmc_free_host(host->mmc);
 
 	return 0;
-- 
1.7.5.4


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

* [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
                       ` (4 preceding siblings ...)
  2014-03-21 16:17     ` [PATCH 5/9] mmc: omap_hsmmc: use devm_ioremap Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-24 12:43       ` Ulf Hansson
  2014-03-21 16:17     ` [PATCH 7/9] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Balaji T K
                       ` (2 subsequent siblings)
  8 siblings, 1 reply; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Andreas Fenkart, Tony Lindgren, Balaji T K

From: Andreas Fenkart <afenkart@gmail.com>

There have been various patches floating around for enabling
the SDIO IRQ for hsmmc, but none of them ever got merged.

Probably the reason for not merging the SDIO interrupt patches
has been the lack of wake-up path for SDIO on some omaps that
has also needed remuxing the SDIO DAT1 line to a GPIO making
the patches complex.

This patch adds the minimal SDIO IRQ support to hsmmc for
omaps that do have the wake-up path. For those omaps, the
DAT1 line need to have the wake-up enable bit set, and the
wake-up interrupt is the same as for the MMC controller.

This patch has been tested on am3730 es1.2 with mwifiex
connected to MMC3 with mwifiex waking to Ethernet traffic
from off-idle mode. Note that for omaps that do not have
the SDIO wake-up path, this patch will not work for idle
modes and further patches for remuxing DAT1 to GPIO are
needed.

Based on earlier patches [1][2] by David Vrabel
<david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
handing improved following how sdhci.c is doing it.

For now, only support SDIO interrupt if we are booted with
a separate wake-irq configued via device tree. This is
because omaps need the wake-irq for idle states, and some
omaps 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.

To use it, you need to specify the wake-irq using the
interrupts-extended property.

[1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
[2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
 include/linux/platform_data/mmc-omap.h |    1 +
 2 files changed, 196 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 38a75bc..0275e0a 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -29,6 +29,7 @@
 #include <linux/timer.h>
 #include <linux/clk.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
 #include <linux/omap-dma.h>
@@ -36,6 +37,7 @@
 #include <linux/mmc/core.h>
 #include <linux/mmc/mmc.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pinctrl/consumer.h>
@@ -106,6 +108,7 @@
 #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)
@@ -140,7 +143,6 @@
 #define VDD_3V0			3000000		/* 300000 uV */
 #define VDD_165_195		(ffs(MMC_VDD_165_195) - 1)
 
-#define AUTO_CMD23		(1 << 1)	/* Auto CMD23 support */
 /*
  * One controller can have multiple slots, like on some omap boards using
  * omap.c controller driver. Luckily this is not currently done on any known
@@ -194,6 +196,7 @@ struct omap_hsmmc_host {
 	u32			sysctl;
 	u32			capa;
 	int			irq;
+	int			wake_irq;
 	int			use_dma, dma_ch;
 	struct dma_chan		*tx_chan;
 	struct dma_chan		*rx_chan;
@@ -206,6 +209,11 @@ struct omap_hsmmc_host {
 	int			req_in_progress;
 	unsigned long		clk_rate;
 	unsigned int		flags;
+#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
+#define AUTO_CMD23		(1 << 1)	/* Auto CMD23 support */
+#define HSMMC_SWAKEUP_QUIRK	(1 << 2)
+#define HSMMC_SDIO_IRQ_ENABLED	(1 << 3)	/* SDIO irq enabled */
+#define HSMMC_WAKE_IRQ_ENABLED	(1 << 4)
 	struct omap_hsmmc_next	next_data;
 	struct	omap_mmc_platform_data	*pdata;
 };
@@ -510,27 +518,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 MMC core */
+	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 but need to keep 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 */
@@ -1117,8 +1138,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);
@@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
+{
+	struct omap_hsmmc_host *host = dev_id;
+	unsigned long flags;
+
+	/* cirq is level triggered, disable to avoid infinite loop */
+	spin_lock_irqsave(&host->irq_lock, flags);
+	if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
+		disable_irq_nosync(host->wake_irq);
+		host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
+	}
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+
+	pm_request_resume(host->dev); /* no use counter */
+
+	return IRQ_HANDLED;
+}
+
 static void set_sd_bus_power(struct omap_hsmmc_host *host)
 {
 	unsigned long i;
@@ -1638,6 +1681,81 @@ 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;
+
+	if (enable)
+		pm_runtime_get_sync(host->dev);
+	spin_lock_irqsave(&host->irq_lock, flags);
+	if (host->flags & HSMMC_RUNTIME_SUSPENDED)
+		goto out;
+
+	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);
+
+out:
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+	if (enable) {
+		pm_runtime_mark_last_busy(host->dev);
+		pm_runtime_put_autosuspend(host->dev);
+	}
+}
+
+static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	int ret;
+
+	/*
+	 * For omaps with wake-up path, wakeirq will be irq from pinctrl and
+	 * for other omaps, wakeirq will be from GPIO (dat line remuxed to
+	 * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
+	 * with functional clock disabled.
+	 */
+	if (!host->dev->of_node || !host->wake_irq)
+		return -ENODEV;
+
+	/* Prevent auto-enabling of IRQ */
+	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
+	ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
+			  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+			  mmc_hostname(mmc), host);
+	if (ret) {
+		dev_err(mmc_dev(host->mmc),
+			"Unable to request wake IRQ\n");
+		return ret;
+	}
+
+	/*
+	 * Some omaps don't have wake-up path from deeper idle states
+	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
+	 */
+	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
+		host->flags |= HSMMC_SWAKEUP_QUIRK;
+
+	return 0;
+}
+
 static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
 {
 	u32 hctl, capa, value;
@@ -1690,7 +1808,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
@@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
 	.reg_offset = 0x100,
 };
 
+static struct omap_mmc_of_data am33xx_mmc_of_data = {
+	.reg_offset = 0x100,
+	.controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
+};
+
 static const struct of_device_id omap_mmc_of_match[] = {
 	{
 		.compatible = "ti,omap2-hsmmc",
@@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
 		.compatible = "ti,omap4-hsmmc",
 		.data = &omap4_mmc_of_data,
 	},
+	{
+		.compatible = "ti,am33xx-hsmmc",
+		.data = &am33xx_mmc_of_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
@@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
 {
 	return ERR_PTR(-EINVAL);
 }
+#define omap_mmc_of_match	NULL
 #endif
 
 static int omap_hsmmc_probe(struct platform_device *pdev)
@@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, host);
 
+	if (pdev->dev.of_node)
+		host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
+
 	mmc->ops	= &omap_hsmmc_ops;
 
 	mmc->f_min = OMAP_MMC_MIN_CLOCK;
@@ -2065,6 +2196,18 @@ 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 have a separate
+	 * wake-up interrupt configured from device tree. This is because
+	 * the wake-up interrupt is needed for idle state and 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.
+	 */
+	ret = omap_hsmmc_configure_wake_irq(host);
+	if (!ret)
+		mmc->caps |= MMC_CAP_SDIO_IRQ;
+
 	omap_hsmmc_protect_card(host);
 
 	mmc_add_host(mmc);
@@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 err_slot_name:
 	mmc_remove_host(mmc);
+	if (host->wake_irq)
+		free_irq(host->wake_irq, host);
 err_irq_cd:
 	if (host->use_reg)
 		omap_hsmmc_reg_put(host);
 err_reg:
 	if (host->pdata->cleanup)
 		host->pdata->cleanup(&pdev->dev);
+	if (host->wake_irq)
+		free_irq(host->wake_irq, host);
 err_irq:
 	if (host->tx_chan)
 		dma_release_channel(host->tx_chan);
@@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
 				OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
 	}
 
+	if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
+		disable_irq(host->wake_irq);
+
 	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
 
@@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
 
 	omap_hsmmc_protect_card(host);
 
+	if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
+		enable_irq(host->wake_irq);
+
 	pm_runtime_mark_last_busy(host->dev);
 	pm_runtime_put_autosuspend(host->dev);
 	return 0;
@@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
 static int omap_hsmmc_runtime_suspend(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
+	int ret = 0;
+	unsigned long flags;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_save(host);
 	dev_dbg(dev, "disabled\n");
 
-	return 0;
+	spin_lock_irqsave(&host->irq_lock, flags);
+	host->flags |= HSMMC_RUNTIME_SUSPENDED;
+	if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
+		OMAP_HSMMC_WRITE(host->base, ISE, 0);
+		OMAP_HSMMC_WRITE(host->base, IE, 0);
+		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+		if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
+			enable_irq(host->wake_irq);
+			host->flags |= HSMMC_WAKE_IRQ_ENABLED;
+		}
+	}
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+
+	return ret;
 }
 
 static int omap_hsmmc_runtime_resume(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
+	int ret = 0;
+	unsigned long flags;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_restore(host);
 	dev_dbg(dev, "enabled\n");
 
-	return 0;
+	spin_lock_irqsave(&host->irq_lock, flags);
+	host->flags &= ~HSMMC_RUNTIME_SUSPENDED;
+	if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
+		if (host->wake_irq && (host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
+			disable_irq_nosync(host->wake_irq);
+			host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
+		}
+
+		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+		OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
+		OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
+	}
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+	return ret;
 }
 
 static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
index 2bf1b30..51e70cf 100644
--- a/include/linux/platform_data/mmc-omap.h
+++ b/include/linux/platform_data/mmc-omap.h
@@ -28,6 +28,7 @@
  */
 #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT		BIT(0)
 #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ	BIT(1)
+#define OMAP_HSMMC_SWAKEUP_MISSING		BIT(2)
 
 struct mmc_card;
 
-- 
1.7.5.4


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

* [PATCH 7/9] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
                       ` (5 preceding siblings ...)
  2014-03-21 16:17     ` [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:17     ` [PATCH 8/9] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Balaji T K
  2014-03-21 16:17     ` [PATCH 9/9] mmc: omap_hsmmc: enable wakeup event for sdio Balaji T K
  8 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Andreas Fenkart, Tony Lindgren, Balaji T K

From: Andreas Fenkart <afenkart@gmail.com>

The am335x can't detect pending cirq in PM runtime suspend.
This patch reconfigures dat1 as a GPIO before going to suspend.
SDIO interrupts are detected with the GPIO, the GPIO will only wake
the module from suspend, SDIO irq detection will still happen through the
IP block.

Idea of remuxing the pins and some minor changes by Tony Lindgren.

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Balaji T K <balajitk@ti.com>
---
 .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   50 +++++++++++++
 drivers/mmc/host/omap_hsmmc.c                      |   74 +++++++++++++++++---
 2 files changed, 113 insertions(+), 11 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index ce80561..0f9b426 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -56,3 +56,53 @@ Examples:
 			&edma 25>;
 		dma-names = "tx", "rx";
 	};
+
+[workaround for missing swakeup on am33xx]
+
+This SOC is missing the swakeup line, it will not detect SDIO irq
+while in suspend.
+
+                             ------
+                             | 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. Since am33xx
+doesn't have this line it never wakes from suspend.
+
+The workaround is to reconfigure the dat1 line as a GPIO upon
+suspend. To make this work, we need to set 1) the named pinctrl
+states "default", "active" and "idle", 2) the gpio detecting
+sdio irq in suspend and 3) compatibe section, see example below.
+The MMC driver will then toggle between active and idle during
+the runtime. If configuration is incomplete, a warning message is
+emitted "falling back to polling".  Mind not every application
+needs SDIO irq, e.g. MMC cards Affected chips are am335x,
+probably others
+
+
+	mmc1: mmc@48060100 {
+		compatible = "ti,am33xx-hsmmc";
+		...
+		interrupts-extended = <&intc 64 &gpio2 28 0>;
+		...
+		pinctrl-names = "default", "active", "idle"
+		pinctrl-0 = <&mmc1_pins>;
+		pinctrl-1 = <&mmc1_pins>;
+		pinctrl-2 = <&mmc1_cirq_pin>;
+		...
+	};
+
+	mmc1_cirq_pin: pinmux_cirq_pin {
+		pinctrl-single,pins = <
+		        0x0f8 0x3f      /* GPIO2_28 */
+		>;
+	};
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 0275e0a..dc23ac7 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -215,6 +215,8 @@ struct omap_hsmmc_host {
 #define HSMMC_SDIO_IRQ_ENABLED	(1 << 3)	/* SDIO irq enabled */
 #define HSMMC_WAKE_IRQ_ENABLED	(1 << 4)
 	struct omap_hsmmc_next	next_data;
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*active;
 	struct	omap_mmc_platform_data	*pdata;
 };
 
@@ -495,6 +497,47 @@ static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
 		gpio_free(pdata->slots[0].switch_pin);
 }
 
+static int omap_hsmmc_pin_init(struct omap_hsmmc_host *host)
+{
+	struct pinctrl *_pinctrl;
+	int ret;
+
+	_pinctrl = devm_pinctrl_get(host->dev);
+	if (IS_ERR(_pinctrl)) {
+		/* okay, if the bootloader set it up right */
+		dev_warn(host->dev, "no pinctrl handle\n");
+		return 0;
+	}
+
+	/* For most cases we don't have wake-ups, and exit after this */
+	host->active = pinctrl_lookup_state(_pinctrl, "active");
+	if (IS_ERR(host->active)) {
+		ret = PTR_ERR(host->active);
+		host->active = NULL;
+		goto done;
+	}
+
+	/* Let's make sure the active and idle states work */
+	ret = pinctrl_pm_select_idle_state(host->dev);
+	if (ret < 0)
+		goto err;
+
+	ret = pinctrl_select_state(_pinctrl, host->active);
+	if (ret < 0)
+		goto err;
+
+	dev_info(mmc_dev(host->mmc), "pins configured for wake-up events\n");
+
+done:
+	host->pinctrl = _pinctrl;
+	return 0;
+
+err:
+	dev_err(mmc_dev(host->mmc), "pins configuration error: %i\n", ret);
+	devm_pinctrl_put(_pinctrl);
+	return ret;
+}
+
 /*
  * Start clock to the card
  */
@@ -1737,7 +1780,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 
 	/* Prevent auto-enabling of IRQ */
 	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
-	ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
+	ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq,
 			  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 			  mmc_hostname(mmc), host);
 	if (ret) {
@@ -1750,8 +1793,13 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 	 * Some omaps don't have wake-up path from deeper idle states
 	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
 	 */
-	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
+	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
+		if (IS_ERR(host->dev->pins->idle_state)) {
+			host->wake_irq = 0;
+			return -EINVAL;
+		}
 		host->flags |= HSMMC_SWAKEUP_QUIRK;
+	}
 
 	return 0;
 }
@@ -1977,7 +2025,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	dma_cap_mask_t mask;
 	unsigned tx_req, rx_req;
-	struct pinctrl *pinctrl;
 	const struct omap_mmc_of_data *data;
 
 	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
@@ -2191,10 +2238,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	omap_hsmmc_disable_irq(host);
 
-	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-	if (IS_ERR(pinctrl))
-		dev_warn(&pdev->dev,
-			"pins are not configured from the driver\n");
+	ret = omap_hsmmc_pin_init(host);
+	if (ret)
+		goto err_irq_cd;
 
 	/*
 	 * For now, only support SDIO interrupt if we have a separate
@@ -2232,16 +2278,14 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
 err_slot_name:
 	mmc_remove_host(mmc);
-	if (host->wake_irq)
-		free_irq(host->wake_irq, host);
+	if (host->pinctrl)
+		devm_pinctrl_put(host->pinctrl);
 err_irq_cd:
 	if (host->use_reg)
 		omap_hsmmc_reg_put(host);
 err_reg:
 	if (host->pdata->cleanup)
 		host->pdata->cleanup(&pdev->dev);
-	if (host->wake_irq)
-		free_irq(host->wake_irq, host);
 err_irq:
 	if (host->tx_chan)
 		dma_release_channel(host->tx_chan);
@@ -2274,6 +2318,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 		dma_release_channel(host->tx_chan);
 	if (host->rx_chan)
 		dma_release_channel(host->rx_chan);
+	if (host->pinctrl)
+		devm_pinctrl_put(host->pinctrl);
 
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
@@ -2380,6 +2426,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 		OMAP_HSMMC_WRITE(host->base, ISE, 0);
 		OMAP_HSMMC_WRITE(host->base, IE, 0);
 		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+		ret = pinctrl_pm_select_idle_state(host->dev);
 		if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
 			enable_irq(host->wake_irq);
 			host->flags |= HSMMC_WAKE_IRQ_ENABLED;
@@ -2408,6 +2455,11 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
 			host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
 		}
 
+		if (host->active) {
+			ret = pinctrl_select_state(host->pinctrl, host->active);
+			if (ret < 0)
+				dev_warn(mmc_dev(host->mmc), "Unable to select active pinmux\n");
+		}
 		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 		OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
 		OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
-- 
1.7.5.4


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

* [PATCH 8/9] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
                       ` (6 preceding siblings ...)
  2014-03-21 16:17     ` [PATCH 7/9] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-03-21 16:17     ` [PATCH 9/9] mmc: omap_hsmmc: enable wakeup event for sdio Balaji T K
  8 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Andreas Fenkart, Tony Lindgren, Balaji T K

From: Andreas Fenkart <afenkart@gmail.com>

Add SDIO IRQ entries to debugfs entry. Note that PSTATE shows current
state of data lines, incl. SDIO IRQ pending

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |   23 ++++++++++++++++++++---
 1 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index dc23ac7..2482783 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -56,6 +56,7 @@
 #define OMAP_HSMMC_RSP54	0x0118
 #define OMAP_HSMMC_RSP76	0x011C
 #define OMAP_HSMMC_DATA		0x0120
+#define OMAP_HSMMC_PSTATE	0x0124
 #define OMAP_HSMMC_HCTL		0x0128
 #define OMAP_HSMMC_SYSCTL	0x012C
 #define OMAP_HSMMC_STAT		0x0130
@@ -1865,14 +1866,30 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
 {
 	struct mmc_host *mmc = s->private;
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
+	const char *cirq_state;
+	bool suspended;
 
-	seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
-			mmc->index, host->context_loss);
+	seq_printf(s, "mmc%d:\n", mmc->index);
+	if (mmc->caps & MMC_CAP_SDIO_IRQ)
+		cirq_state = (host->flags & HSMMC_SDIO_IRQ_ENABLED) ?
+			"enabled" : "disabled";
+	else
+		cirq_state = "polling";
+	seq_printf(s, "sdio irq\t%s\n", cirq_state);
 
-	pm_runtime_get_sync(host->dev);
+	if (host->flags & HSMMC_SWAKEUP_QUIRK) {
+		suspended = host->dev->power.runtime_status != RPM_ACTIVE;
+		seq_printf(s, "pinmux config\t%s\n", (suspended ?
+						      "gpio" : "sdio"));
+	}
+	seq_printf(s, "ctx_loss:\t%d\n", host->context_loss);
 
+	pm_runtime_get_sync(host->dev);
+	seq_puts(s, "\nregs:\n");
 	seq_printf(s, "CON:\t\t0x%08x\n",
 			OMAP_HSMMC_READ(host->base, CON));
+	seq_printf(s, "PSTATE:\t\t0x%08x\n",
+		   OMAP_HSMMC_READ(host->base, PSTATE));
 	seq_printf(s, "HCTL:\t\t0x%08x\n",
 			OMAP_HSMMC_READ(host->base, HCTL));
 	seq_printf(s, "SYSCTL:\t\t0x%08x\n",
-- 
1.7.5.4


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

* [PATCH 9/9] mmc: omap_hsmmc: enable wakeup event for sdio
  2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
                       ` (7 preceding siblings ...)
  2014-03-21 16:17     ` [PATCH 8/9] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Balaji T K
@ 2014-03-21 16:17     ` Balaji T K
  2014-05-02 15:33       ` Balaji T K
  8 siblings, 1 reply; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:17 UTC (permalink / raw)
  To: linux-mmc, chris; +Cc: linux-omap, Balaji T K

To detect sdio irqs properly without spurious events,
OMAP4 needs IWE in CON and CTPL, CLKEXTFREE in HCTL to be set

Signed-off-by: Balaji T K <balajitk@ti.com>
---
 drivers/mmc/host/omap_hsmmc.c |   13 ++++++++++++-
 1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 2482783..120f7cf 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -94,7 +94,10 @@
 #define BCE			(1 << 1)
 #define FOUR_BIT		(1 << 1)
 #define HSPE			(1 << 2)
+#define IWE			(1 << 24)
 #define DDR			(1 << 19)
+#define CLKEXTFREE		(1 << 16)
+#define CTPL			(1 << 11)
 #define DW8			(1 << 5)
 #define OD			0x1
 #define STAT_CLEAR		0xFFFFFFFF
@@ -732,6 +735,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		capa = VS18;
 	}
 
+	if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
+		hctl |= IWE;
 	OMAP_HSMMC_WRITE(host->base, HCTL,
 			OMAP_HSMMC_READ(host->base, HCTL) | hctl);
 
@@ -1728,7 +1733,7 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_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;
+	u32 irq_mask, con;
 	unsigned long flags;
 
 	if (enable)
@@ -1737,14 +1742,18 @@ static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 	if (host->flags & HSMMC_RUNTIME_SUSPENDED)
 		goto out;
 
+	con = OMAP_HSMMC_READ(host->base, CON);
 	irq_mask = OMAP_HSMMC_READ(host->base, ISE);
 	if (enable) {
 		host->flags |= HSMMC_SDIO_IRQ_ENABLED;
 		irq_mask |= CIRQ_EN;
+		con |= CTPL | CLKEXTFREE;
 	} else {
 		host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
 		irq_mask &= ~CIRQ_EN;
+		con &= ~(CTPL | CLKEXTFREE);
 	}
+	OMAP_HSMMC_WRITE(host->base, CON, con);
 	OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
 
 	/*
@@ -1801,6 +1810,8 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 		}
 		host->flags |= HSMMC_SWAKEUP_QUIRK;
 	}
+	OMAP_HSMMC_WRITE(host->base, HCTL,
+			 OMAP_HSMMC_READ(host->base, HCTL) | IWE);
 
 	return 0;
 }
-- 
1.7.5.4


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

* Re: [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region
  2014-03-21 16:17     ` [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region Balaji T K
@ 2014-03-21 16:18       ` Felipe Balbi
  2014-03-21 16:27         ` Balaji T K
  0 siblings, 1 reply; 28+ messages in thread
From: Felipe Balbi @ 2014-03-21 16:18 UTC (permalink / raw)
  To: Balaji T K; +Cc: linux-mmc, chris, linux-omap

[-- Attachment #1: Type: text/plain, Size: 929 bytes --]

On Fri, Mar 21, 2014 at 09:47:33PM +0530, Balaji T K wrote:
> With devm_request_mem_region conversion release_mem_region can be
> removed in clean up path
> 
> Signed-off-by: Balaji T K <balajitk@ti.com>
> ---
>  drivers/mmc/host/omap_hsmmc.c |   11 ++---------
>  1 files changed, 2 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 8e35a6e..9952673 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -1881,7 +1881,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  	if (res == NULL || irq < 0)
>  		return -ENXIO;
>  
> -	res = request_mem_region(res->start, resource_size(res), pdev->name);
> +	res = devm_request_mem_region(&pdev->dev, res->start,
> +					resource_size(res), pdev->name);

while at that, why don't you switch over to devm_ioremap_resource()
already ?

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region
  2014-03-21 16:18       ` Felipe Balbi
@ 2014-03-21 16:27         ` Balaji T K
  2014-03-21 16:30           ` Felipe Balbi
  0 siblings, 1 reply; 28+ messages in thread
From: Balaji T K @ 2014-03-21 16:27 UTC (permalink / raw)
  To: balbi; +Cc: linux-mmc, chris, linux-omap

On Friday 21 March 2014 09:48 PM, Felipe Balbi wrote:
> On Fri, Mar 21, 2014 at 09:47:33PM +0530, Balaji T K wrote:
>> With devm_request_mem_region conversion release_mem_region can be
>> removed in clean up path
>>
>> Signed-off-by: Balaji T K <balajitk@ti.com>
>> ---
>>   drivers/mmc/host/omap_hsmmc.c |   11 ++---------
>>   1 files changed, 2 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>> index 8e35a6e..9952673 100644
>> --- a/drivers/mmc/host/omap_hsmmc.c
>> +++ b/drivers/mmc/host/omap_hsmmc.c
>> @@ -1881,7 +1881,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>   	if (res == NULL || irq < 0)
>>   		return -ENXIO;
>>
>> -	res = request_mem_region(res->start, resource_size(res), pdev->name);
>> +	res = devm_request_mem_region(&pdev->dev, res->start,
>> +					resource_size(res), pdev->name);
>
> while at that, why don't you switch over to devm_ioremap_resource()
> already ?

I can't do that because of 0x100 reg_offset on OMAP4+ and am335x+

Thanks and Regards,
Balaji T K

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

* Re: [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region
  2014-03-21 16:27         ` Balaji T K
@ 2014-03-21 16:30           ` Felipe Balbi
  0 siblings, 0 replies; 28+ messages in thread
From: Felipe Balbi @ 2014-03-21 16:30 UTC (permalink / raw)
  To: Balaji T K; +Cc: balbi, linux-mmc, chris, linux-omap

[-- Attachment #1: Type: text/plain, Size: 1275 bytes --]

On Fri, Mar 21, 2014 at 09:57:52PM +0530, Balaji T K wrote:
> On Friday 21 March 2014 09:48 PM, Felipe Balbi wrote:
> >On Fri, Mar 21, 2014 at 09:47:33PM +0530, Balaji T K wrote:
> >>With devm_request_mem_region conversion release_mem_region can be
> >>removed in clean up path
> >>
> >>Signed-off-by: Balaji T K <balajitk@ti.com>
> >>---
> >>  drivers/mmc/host/omap_hsmmc.c |   11 ++---------
> >>  1 files changed, 2 insertions(+), 9 deletions(-)
> >>
> >>diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> >>index 8e35a6e..9952673 100644
> >>--- a/drivers/mmc/host/omap_hsmmc.c
> >>+++ b/drivers/mmc/host/omap_hsmmc.c
> >>@@ -1881,7 +1881,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
> >>  	if (res == NULL || irq < 0)
> >>  		return -ENXIO;
> >>
> >>-	res = request_mem_region(res->start, resource_size(res), pdev->name);
> >>+	res = devm_request_mem_region(&pdev->dev, res->start,
> >>+					resource_size(res), pdev->name);
> >
> >while at that, why don't you switch over to devm_ioremap_resource()
> >already ?
> 
> I can't do that because of 0x100 reg_offset on OMAP4+ and am335x+

split the resource into two parts, one is OMAP-specific and the other is
the generic SDHCI start.

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-21 16:10   ` Balaji T K
@ 2014-03-24  9:30     ` Dmitry Lifshitz
  2014-04-18 16:46       ` Tony Lindgren
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Lifshitz @ 2014-03-24  9:30 UTC (permalink / raw)
  To: Balaji T K
  Cc: Andreas Fenkart, Tony Lindgren, Chris Ball, Grant Likely,
	Felipe Balbi, zonque, galak, linux-doc, linux-mmc, linux-omap

Hi,

I've tested the branch omap_hsmmc_sdio_irq_devm_cleanup on custom OMAP5 
based board.
We have mwifiex connected to MMC3.

I used two approaches:

* Using dat1 line as GPIO with dynamic remuxing
* Using separate GPIO connected to dat1 with static mux settings

Both works just fine. Thanks Andreas for a great solution.

I have just one issue - how to define GPIO IRQ using DT in OMAP5 case.
As a temporary hack for testing I used a fixed GPIO number and 
gpio_to_irq() call in omap_hsmmc_configure_wake_irq().

OMAP5 MMC3 has the following DT entry (omap5.dtsi):

                 mmc3: mmc@480ad000 {
                         compatible = "ti,omap4-hsmmc";
                         reg = <0x480ad000 0x400>;
                         interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
                         ti,hwmods = "mmc3";
                         ti,needs-special-reset;
                         dmas = <&sdma 77>, <&sdma 78>;
                         dma-names = "tx", "rx";
                 };

What should be by board MMC3 setup?

Something like this?

&mmc3 {
         pinctrl-names = "default", "active", "idle";
         pinctrl-0 = <&mmc3_pins>;
         pinctrl-1 = <&mmc3_pins>;
         pinctrl-2 = <&mmc3_cirq_pin>;

         interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>,
                             <????????>;

         vmmc-supply = <&ldo2_reg>;
         bus-width = <4>;
         ti,non-removable;
};

I tried to specify the second interrupt as (wlsdio_data1 / gpio5_131):

<&gpio5 3 IRQ_TYPE_NONE>

but it fails:

[    2.275777] ------------[ cut here ]------------
[    2.280604] WARNING: CPU: 1 PID: 6 at 
/home/lifshitz/workroot/OMAP5-eewiki/omap5-kernel/kernel/irq/manage.c:1418 
request_threaded_irq+0x11c/0x12c()
[    2.294414] Modules linked in:
[    2.297611] CPU: 1 PID: 6 Comm: kworker/u4:0 Tainted: G W    
3.14.0-rc4-cm-t54-test-suit+ #30
[    2.307165] Workqueue: deferwq deferred_probe_work_func
[    2.312629] [<c001597c>] (unwind_backtrace) from [<c001283c>] 
(show_stack+0x10/0x14)
[    2.320739] [<c001283c>] (show_stack) from [<c06494dc>] 
(dump_stack+0x70/0x88)
[    2.328294] [<c06494dc>] (dump_stack) from [<c003a520>] 
(warn_slowpath_common+0x70/0x88)
[    2.336761] [<c003a520>] (warn_slowpath_common) from [<c003a554>] 
(warn_slowpath_null+0x1c/0x24)
[    2.345940] [<c003a554>] (warn_slowpath_null) from [<c0083ce4>] 
(request_threaded_irq+0x11c/0x12c)
[    2.355312] [<c0083ce4>] (request_threaded_irq) from [<c0085964>] 
(devm_request_threaded_irq+0x5c/0x90)
[    2.361973] mmc1: host does not support reading read-only switch. 
assuming write-enable.
[    2.362020] mmc1: new SDHC card at address e624
[    2.362331] mmcblk1: mmc1:e624 SU08G 7.40 GiB
[    2.368152]  mmcblk1: p1 p2
[    2.385850] [<c0085964>] (devm_request_threaded_irq) from 
[<c047d958>] (omap_hsmmc_configure_wake_irq+0x7c/0xfc)
[    2.393150] usb 1-2: New USB device found, idVendor=0424, idProduct=3503
[    2.393156] usb 1-2: New USB device strings: Mfr=0, Product=0, 
SerialNumber=0
[    2.393674] hub 1-2:1.0: USB hub found
[    2.393769] hub 1-2:1.0: 3 ports detected
[    2.419038] [<c047d958>] (omap_hsmmc_configure_wake_irq) from 
[<c047ea28>] (omap_hsmmc_probe+0x5b4/0x854)
[    2.429052] [<c047ea28>] (omap_hsmmc_probe) from [<c0359ec0>] 
(platform_drv_probe+0x18/0x48)
[    2.437879] [<c0359ec0>] (platform_drv_probe) from [<c03580c8>] 
(really_probe+0x80/0x208)
[    2.446427] [<c03580c8>] (really_probe) from [<c0358360>] 
(driver_probe_device+0x30/0x48)
[    2.454976] [<c0358360>] (driver_probe_device) from [<c0356a58>] 
(bus_for_each_drv+0x5c/0x88)
[    2.463891] [<c0356a58>] (bus_for_each_drv) from [<c03582f4>] 
(device_attach+0x80/0xa0)
[    2.472247] [<c03582f4>] (device_attach) from [<c03577b4>] 
(bus_probe_device+0x84/0xa8)
[    2.480617] [<c03577b4>] (bus_probe_device) from [<c0357bb8>] 
(deferred_probe_work_func+0x68/0x98)
[    2.489987] [<c0357bb8>] (deferred_probe_work_func) from [<c00533c0>] 
(process_one_work+0x150/0x41c)
[    2.499542] [<c00533c0>] (process_one_work) from [<c0053d10>] 
(worker_thread+0xf4/0x31c)
[    2.508011] [<c0053d10>] (worker_thread) from [<c0059994>] 
(kthread+0xd4/0xe8)
[    2.512785] usb 1-3: new high-speed USB device number 3 using ehci-omap
[    2.522474] [<c0059994>] (kthread) from [<c000e878>] 
(ret_from_fork+0x14/0x3c)
[    2.530023] ---[ end trace 8d60f1d3adba88d7 ]---


Regards,

Dmitry

On 03/21/2014 06:10 PM, Balaji T K wrote:
> On Friday 21 March 2014 05:50 PM, Andreas Fenkart wrote:
>
> Thanks Andreas for the patch series
>
> I rebased against latest mmc-next, made few changes to your patch.
> I have hosted your series along with devm cleanups on a branch[1] for 
> testing
> [1]
> git://git.ti.com/~balajitk/ti-linux-kernel/omap-hsmmc.git 
> omap_hsmmc_sdio_irq_devm_cleanup
>
> Can you please test on your platform and provide feedback.
>
> Details about the changes below.


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

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-21 16:17     ` [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt Balaji T K
@ 2014-03-24 12:43       ` Ulf Hansson
  2014-03-24 14:59         ` Andreas Fenkart
  0 siblings, 1 reply; 28+ messages in thread
From: Ulf Hansson @ 2014-03-24 12:43 UTC (permalink / raw)
  To: Balaji T K, Andreas Fenkart, Tony Lindgren
  Cc: linux-mmc, Chris Ball, linux-omap

On 21 March 2014 17:17, Balaji T K <balajitk@ti.com> wrote:
> From: Andreas Fenkart <afenkart@gmail.com>
>
> There have been various patches floating around for enabling
> the SDIO IRQ for hsmmc, but none of them ever got merged.
>
> Probably the reason for not merging the SDIO interrupt patches
> has been the lack of wake-up path for SDIO on some omaps that
> has also needed remuxing the SDIO DAT1 line to a GPIO making
> the patches complex.
>
> This patch adds the minimal SDIO IRQ support to hsmmc for
> omaps that do have the wake-up path. For those omaps, the
> DAT1 line need to have the wake-up enable bit set, and the
> wake-up interrupt is the same as for the MMC controller.
>
> This patch has been tested on am3730 es1.2 with mwifiex
> connected to MMC3 with mwifiex waking to Ethernet traffic
> from off-idle mode. Note that for omaps that do not have
> the SDIO wake-up path, this patch will not work for idle
> modes and further patches for remuxing DAT1 to GPIO are
> needed.
>
> Based on earlier patches [1][2] by David Vrabel
> <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
> and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
> handing improved following how sdhci.c is doing it.
>
> For now, only support SDIO interrupt if we are booted with
> a separate wake-irq configued via device tree. This is
> because omaps need the wake-irq for idle states, and some
> omaps 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.
>
> To use it, you need to specify the wake-irq using the
> interrupts-extended property.
>
> [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
> [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446
>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> Signed-off-by: Balaji T K <balajitk@ti.com>
> ---
>  drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
>  include/linux/platform_data/mmc-omap.h |    1 +
>  2 files changed, 196 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 38a75bc..0275e0a 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -29,6 +29,7 @@
>  #include <linux/timer.h>
>  #include <linux/clk.h>
>  #include <linux/of.h>
> +#include <linux/of_irq.h>
>  #include <linux/of_gpio.h>
>  #include <linux/of_device.h>
>  #include <linux/omap-dma.h>
> @@ -36,6 +37,7 @@
>  #include <linux/mmc/core.h>
>  #include <linux/mmc/mmc.h>
>  #include <linux/io.h>
> +#include <linux/irq.h>
>  #include <linux/gpio.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/pinctrl/consumer.h>
> @@ -106,6 +108,7 @@
>  #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)
> @@ -140,7 +143,6 @@
>  #define VDD_3V0                        3000000         /* 300000 uV */
>  #define VDD_165_195            (ffs(MMC_VDD_165_195) - 1)
>
> -#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>  /*
>   * One controller can have multiple slots, like on some omap boards using
>   * omap.c controller driver. Luckily this is not currently done on any known
> @@ -194,6 +196,7 @@ struct omap_hsmmc_host {
>         u32                     sysctl;
>         u32                     capa;
>         int                     irq;
> +       int                     wake_irq;
>         int                     use_dma, dma_ch;
>         struct dma_chan         *tx_chan;
>         struct dma_chan         *rx_chan;
> @@ -206,6 +209,11 @@ struct omap_hsmmc_host {
>         int                     req_in_progress;
>         unsigned long           clk_rate;
>         unsigned int            flags;
> +#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
> +#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
> +#define HSMMC_SWAKEUP_QUIRK    (1 << 2)
> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 3)        /* SDIO irq enabled */
> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 4)
>         struct omap_hsmmc_next  next_data;
>         struct  omap_mmc_platform_data  *pdata;
>  };
> @@ -510,27 +518,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 MMC core */
> +       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 but need to keep 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 */
> @@ -1117,8 +1138,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);
> @@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>         return IRQ_HANDLED;
>  }
>
> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
> +{
> +       struct omap_hsmmc_host *host = dev_id;
> +       unsigned long flags;
> +
> +       /* cirq is level triggered, disable to avoid infinite loop */
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
> +               disable_irq_nosync(host->wake_irq);
> +               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
> +       }
> +       spin_unlock_irqrestore(&host->irq_lock, flags);
> +
> +       pm_request_resume(host->dev); /* no use counter */
> +
> +       return IRQ_HANDLED;
> +}
> +
>  static void set_sd_bus_power(struct omap_hsmmc_host *host)
>  {
>         unsigned long i;
> @@ -1638,6 +1681,81 @@ 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;
> +
> +       if (enable)
> +               pm_runtime_get_sync(host->dev);
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       if (host->flags & HSMMC_RUNTIME_SUSPENDED)
> +               goto out;

This seems wrong to me.

What if the host is suspended (runtime PM wise) and you want to
disable SDIO irq.
Won't SDIO irq be kept enabled in this case?

> +
> +       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);
> +
> +out:
> +       spin_unlock_irqrestore(&host->irq_lock, flags);
> +       if (enable) {
> +               pm_runtime_mark_last_busy(host->dev);
> +               pm_runtime_put_autosuspend(host->dev);
> +       }
> +}
> +
> +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
> +{
> +       struct mmc_host *mmc = host->mmc;
> +       int ret;
> +
> +       /*
> +        * For omaps with wake-up path, wakeirq will be irq from pinctrl and
> +        * for other omaps, wakeirq will be from GPIO (dat line remuxed to
> +        * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
> +        * with functional clock disabled.
> +        */
> +       if (!host->dev->of_node || !host->wake_irq)
> +               return -ENODEV;
> +
> +       /* Prevent auto-enabling of IRQ */
> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
> +       ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
> +                         IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> +                         mmc_hostname(mmc), host);
> +       if (ret) {
> +               dev_err(mmc_dev(host->mmc),
> +                       "Unable to request wake IRQ\n");
> +               return ret;
> +       }
> +
> +       /*
> +        * Some omaps don't have wake-up path from deeper idle states
> +        * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
> +        */
> +       if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
> +               host->flags |= HSMMC_SWAKEUP_QUIRK;
> +
> +       return 0;
> +}
> +
>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>  {
>         u32 hctl, capa, value;
> @@ -1690,7 +1808,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
> @@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
>         .reg_offset = 0x100,
>  };
>
> +static struct omap_mmc_of_data am33xx_mmc_of_data = {
> +       .reg_offset = 0x100,
> +       .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
> +};
> +
>  static const struct of_device_id omap_mmc_of_match[] = {
>         {
>                 .compatible = "ti,omap2-hsmmc",
> @@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
>                 .compatible = "ti,omap4-hsmmc",
>                 .data = &omap4_mmc_of_data,
>         },
> +       {
> +               .compatible = "ti,am33xx-hsmmc",
> +               .data = &am33xx_mmc_of_data,
> +       },
>         {},
>  };
>  MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
> @@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
>  {
>         return ERR_PTR(-EINVAL);
>  }
> +#define omap_mmc_of_match      NULL
>  #endif
>
>  static int omap_hsmmc_probe(struct platform_device *pdev)
> @@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>
>         platform_set_drvdata(pdev, host);
>
> +       if (pdev->dev.of_node)
> +               host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
> +
>         mmc->ops        = &omap_hsmmc_ops;
>
>         mmc->f_min = OMAP_MMC_MIN_CLOCK;
> @@ -2065,6 +2196,18 @@ 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 have a separate
> +        * wake-up interrupt configured from device tree. This is because
> +        * the wake-up interrupt is needed for idle state and 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.
> +        */
> +       ret = omap_hsmmc_configure_wake_irq(host);
> +       if (!ret)
> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
> +
>         omap_hsmmc_protect_card(host);
>
>         mmc_add_host(mmc);
> @@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>
>  err_slot_name:
>         mmc_remove_host(mmc);
> +       if (host->wake_irq)
> +               free_irq(host->wake_irq, host);
>  err_irq_cd:
>         if (host->use_reg)
>                 omap_hsmmc_reg_put(host);
>  err_reg:
>         if (host->pdata->cleanup)
>                 host->pdata->cleanup(&pdev->dev);
> +       if (host->wake_irq)
> +               free_irq(host->wake_irq, host);
>  err_irq:
>         if (host->tx_chan)
>                 dma_release_channel(host->tx_chan);
> @@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
>                                 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>         }
>
> +       if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
> +               disable_irq(host->wake_irq);
> +
>         if (host->dbclk)
>                 clk_disable_unprepare(host->dbclk);
>
> @@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
>
>         omap_hsmmc_protect_card(host);
>
> +       if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
> +               enable_irq(host->wake_irq);
> +
>         pm_runtime_mark_last_busy(host->dev);
>         pm_runtime_put_autosuspend(host->dev);
>         return 0;
> @@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
>  static int omap_hsmmc_runtime_suspend(struct device *dev)
>  {
>         struct omap_hsmmc_host *host;
> +       int ret = 0;
> +       unsigned long flags;
>
>         host = platform_get_drvdata(to_platform_device(dev));
>         omap_hsmmc_context_save(host);
>         dev_dbg(dev, "disabled\n");
>
> -       return 0;
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       host->flags |= HSMMC_RUNTIME_SUSPENDED;
> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +               if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
> +                       enable_irq(host->wake_irq);
> +                       host->flags |= HSMMC_WAKE_IRQ_ENABLED;
> +               }
> +       }
> +       spin_unlock_irqrestore(&host->irq_lock, flags);

This seems like code you need to execute during system suspend as
well? How will you else make sure the wakeup irq is fetched in this
scenario?

Maybe, you have a work around for this problem in the omap power domain?

Kind regards
Ulf Hansson

> +
> +       return ret;
>  }
>
>  static int omap_hsmmc_runtime_resume(struct device *dev)
>  {
>         struct omap_hsmmc_host *host;
> +       int ret = 0;
> +       unsigned long flags;
>
>         host = platform_get_drvdata(to_platform_device(dev));
>         omap_hsmmc_context_restore(host);
>         dev_dbg(dev, "enabled\n");
>
> -       return 0;
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       host->flags &= ~HSMMC_RUNTIME_SUSPENDED;
> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
> +               if (host->wake_irq && (host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
> +                       disable_irq_nosync(host->wake_irq);
> +                       host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
> +               }
> +
> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +               OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
> +               OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
> +       }
> +       spin_unlock_irqrestore(&host->irq_lock, flags);
> +       return ret;
>  }
>
>  static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
> diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
> index 2bf1b30..51e70cf 100644
> --- a/include/linux/platform_data/mmc-omap.h
> +++ b/include/linux/platform_data/mmc-omap.h
> @@ -28,6 +28,7 @@
>   */
>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT          BIT(0)
>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ      BIT(1)
> +#define OMAP_HSMMC_SWAKEUP_MISSING             BIT(2)
>
>  struct mmc_card;
>
> --
> 1.7.5.4
>
> --
> 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] 28+ messages in thread

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-24 12:43       ` Ulf Hansson
@ 2014-03-24 14:59         ` Andreas Fenkart
  2014-03-24 16:02           ` Ulf Hansson
  0 siblings, 1 reply; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-24 14:59 UTC (permalink / raw)
  To: Ulf Hansson; +Cc: Balaji T K, Tony Lindgren, linux-mmc, Chris Ball, linux-omap

Hi,

2014-03-24 13:43 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
> On 21 March 2014 17:17, Balaji T K <balajitk@ti.com> wrote:
>> From: Andreas Fenkart <afenkart@gmail.com>
>>
>> There have been various patches floating around for enabling
>> the SDIO IRQ for hsmmc, but none of them ever got merged.
>>
>> Probably the reason for not merging the SDIO interrupt patches
>> has been the lack of wake-up path for SDIO on some omaps that
>> has also needed remuxing the SDIO DAT1 line to a GPIO making
>> the patches complex.
>>
>> This patch adds the minimal SDIO IRQ support to hsmmc for
>> omaps that do have the wake-up path. For those omaps, the
>> DAT1 line need to have the wake-up enable bit set, and the
>> wake-up interrupt is the same as for the MMC controller.
>>
>> This patch has been tested on am3730 es1.2 with mwifiex
>> connected to MMC3 with mwifiex waking to Ethernet traffic
>> from off-idle mode. Note that for omaps that do not have
>> the SDIO wake-up path, this patch will not work for idle
>> modes and further patches for remuxing DAT1 to GPIO are
>> needed.
>>
>> Based on earlier patches [1][2] by David Vrabel
>> <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
>> and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
>> handing improved following how sdhci.c is doing it.
>>
>> For now, only support SDIO interrupt if we are booted with
>> a separate wake-irq configued via device tree. This is
>> because omaps need the wake-irq for idle states, and some
>> omaps 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.
>>
>> To use it, you need to specify the wake-irq using the
>> interrupts-extended property.
>>
>> [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
>> [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446
>>
>> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>> Signed-off-by: Tony Lindgren <tony@atomide.com>
>> Signed-off-by: Balaji T K <balajitk@ti.com>
>> ---
>>  drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
>>  include/linux/platform_data/mmc-omap.h |    1 +
>>  2 files changed, 196 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>> index 38a75bc..0275e0a 100644
>> --- a/drivers/mmc/host/omap_hsmmc.c
>> +++ b/drivers/mmc/host/omap_hsmmc.c
>> @@ -29,6 +29,7 @@
>>  #include <linux/timer.h>
>>  #include <linux/clk.h>
>>  #include <linux/of.h>
>> +#include <linux/of_irq.h>
>>  #include <linux/of_gpio.h>
>>  #include <linux/of_device.h>
>>  #include <linux/omap-dma.h>
>> @@ -36,6 +37,7 @@
>>  #include <linux/mmc/core.h>
>>  #include <linux/mmc/mmc.h>
>>  #include <linux/io.h>
>> +#include <linux/irq.h>
>>  #include <linux/gpio.h>
>>  #include <linux/regulator/consumer.h>
>>  #include <linux/pinctrl/consumer.h>
>> @@ -106,6 +108,7 @@
>>  #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)
>> @@ -140,7 +143,6 @@
>>  #define VDD_3V0                        3000000         /* 300000 uV */
>>  #define VDD_165_195            (ffs(MMC_VDD_165_195) - 1)
>>
>> -#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>  /*
>>   * One controller can have multiple slots, like on some omap boards using
>>   * omap.c controller driver. Luckily this is not currently done on any known
>> @@ -194,6 +196,7 @@ struct omap_hsmmc_host {
>>         u32                     sysctl;
>>         u32                     capa;
>>         int                     irq;
>> +       int                     wake_irq;
>>         int                     use_dma, dma_ch;
>>         struct dma_chan         *tx_chan;
>>         struct dma_chan         *rx_chan;
>> @@ -206,6 +209,11 @@ struct omap_hsmmc_host {
>>         int                     req_in_progress;
>>         unsigned long           clk_rate;
>>         unsigned int            flags;
>> +#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
>> +#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */

merge conflict here? I do not use HSMMC_RUNTIME_SUSPEND anymore
and of course, neither do I define AUTO_CMD23. :-)

>> +#define HSMMC_SWAKEUP_QUIRK    (1 << 2)
>> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 3)        /* SDIO irq enabled */
>> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 4)
>>         struct omap_hsmmc_next  next_data;
>>         struct  omap_mmc_platform_data  *pdata;
>>  };
>> @@ -510,27 +518,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 MMC core */
>> +       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 but need to keep 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 */
>> @@ -1117,8 +1138,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);
>> @@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>>         return IRQ_HANDLED;
>>  }
>>
>> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
>> +{
>> +       struct omap_hsmmc_host *host = dev_id;
>> +       unsigned long flags;
>> +
>> +       /* cirq is level triggered, disable to avoid infinite loop */
>> +       spin_lock_irqsave(&host->irq_lock, flags);
>> +       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
>> +               disable_irq_nosync(host->wake_irq);
>> +               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>> +       }
>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>> +
>> +       pm_request_resume(host->dev); /* no use counter */
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>>  static void set_sd_bus_power(struct omap_hsmmc_host *host)
>>  {
>>         unsigned long i;
>> @@ -1638,6 +1681,81 @@ 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;
>> +
>> +       if (enable)
>> +               pm_runtime_get_sync(host->dev);
>> +       spin_lock_irqsave(&host->irq_lock, flags);
>> +       if (host->flags & HSMMC_RUNTIME_SUSPENDED)
>> +               goto out;
>
> This seems wrong to me.
>
> What if the host is suspended (runtime PM wise) and you want to
> disable SDIO irq.
> Won't SDIO irq be kept enabled in this case?

Whenever you enter this function the module is not suspended,
mind that register accesses will fail without fclk. So before entering here,
there must have been a  pm_runtime_resume somewhere.

>
>> +
>> +       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);
>> +
>> +out:
>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>> +       if (enable) {
>> +               pm_runtime_mark_last_busy(host->dev);
>> +               pm_runtime_put_autosuspend(host->dev);
>> +       }
>> +}
>> +
>> +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>> +{
>> +       struct mmc_host *mmc = host->mmc;
>> +       int ret;
>> +
>> +       /*
>> +        * For omaps with wake-up path, wakeirq will be irq from pinctrl and
>> +        * for other omaps, wakeirq will be from GPIO (dat line remuxed to
>> +        * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
>> +        * with functional clock disabled.
>> +        */
>> +       if (!host->dev->of_node || !host->wake_irq)
>> +               return -ENODEV;
>> +
>> +       /* Prevent auto-enabling of IRQ */
>> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
>> +       ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
>> +                         IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>> +                         mmc_hostname(mmc), host);
>> +       if (ret) {
>> +               dev_err(mmc_dev(host->mmc),
>> +                       "Unable to request wake IRQ\n");
>> +               return ret;
>> +       }
>> +
>> +       /*
>> +        * Some omaps don't have wake-up path from deeper idle states
>> +        * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
>> +        */
>> +       if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
>> +               host->flags |= HSMMC_SWAKEUP_QUIRK;
>> +
>> +       return 0;
>> +}
>> +
>>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>>  {
>>         u32 hctl, capa, value;
>> @@ -1690,7 +1808,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
>> @@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
>>         .reg_offset = 0x100,
>>  };
>>
>> +static struct omap_mmc_of_data am33xx_mmc_of_data = {
>> +       .reg_offset = 0x100,
>> +       .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
>> +};
>> +
>>  static const struct of_device_id omap_mmc_of_match[] = {
>>         {
>>                 .compatible = "ti,omap2-hsmmc",
>> @@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
>>                 .compatible = "ti,omap4-hsmmc",
>>                 .data = &omap4_mmc_of_data,
>>         },
>> +       {
>> +               .compatible = "ti,am33xx-hsmmc",
>> +               .data = &am33xx_mmc_of_data,
>> +       },
>>         {},
>>  };
>>  MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
>> @@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
>>  {
>>         return ERR_PTR(-EINVAL);
>>  }
>> +#define omap_mmc_of_match      NULL
>>  #endif
>>
>>  static int omap_hsmmc_probe(struct platform_device *pdev)
>> @@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>
>>         platform_set_drvdata(pdev, host);
>>
>> +       if (pdev->dev.of_node)
>> +               host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
>> +
>>         mmc->ops        = &omap_hsmmc_ops;
>>
>>         mmc->f_min = OMAP_MMC_MIN_CLOCK;
>> @@ -2065,6 +2196,18 @@ 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 have a separate
>> +        * wake-up interrupt configured from device tree. This is because
>> +        * the wake-up interrupt is needed for idle state and 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.
>> +        */
>> +       ret = omap_hsmmc_configure_wake_irq(host);
>> +       if (!ret)
>> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
>> +
>>         omap_hsmmc_protect_card(host);
>>
>>         mmc_add_host(mmc);
>> @@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>
>>  err_slot_name:
>>         mmc_remove_host(mmc);
>> +       if (host->wake_irq)
>> +               free_irq(host->wake_irq, host);
>>  err_irq_cd:
>>         if (host->use_reg)
>>                 omap_hsmmc_reg_put(host);
>>  err_reg:
>>         if (host->pdata->cleanup)
>>                 host->pdata->cleanup(&pdev->dev);
>> +       if (host->wake_irq)
>> +               free_irq(host->wake_irq, host);
>>  err_irq:
>>         if (host->tx_chan)
>>                 dma_release_channel(host->tx_chan);
>> @@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
>>                                 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>>         }
>>
>> +       if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>> +               disable_irq(host->wake_irq);
>> +
>>         if (host->dbclk)
>>                 clk_disable_unprepare(host->dbclk);
>>
>> @@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
>>
>>         omap_hsmmc_protect_card(host);
>>
>> +       if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>> +               enable_irq(host->wake_irq);
>> +
>>         pm_runtime_mark_last_busy(host->dev);
>>         pm_runtime_put_autosuspend(host->dev);
>>         return 0;
>> @@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
>>  static int omap_hsmmc_runtime_suspend(struct device *dev)
>>  {
>>         struct omap_hsmmc_host *host;
>> +       int ret = 0;
>> +       unsigned long flags;
>>
>>         host = platform_get_drvdata(to_platform_device(dev));
>>         omap_hsmmc_context_save(host);
>>         dev_dbg(dev, "disabled\n");
>>
>> -       return 0;
>> +       spin_lock_irqsave(&host->irq_lock, flags);
>> +       host->flags |= HSMMC_RUNTIME_SUSPENDED;
>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
>> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>> +               if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>> +                       enable_irq(host->wake_irq);
>> +                       host->flags |= HSMMC_WAKE_IRQ_ENABLED;
>> +               }
>> +       }
>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>
> This seems like code you need to execute during system suspend as
> well? How will you else make sure the wakeup irq is fetched in this
> scenario?
>
> Maybe, you have a work around for this problem in the omap power domain?

I just added printks in resume and pm_runtime_resume, and did a
suspend/resume cycle:

[   42.853308] net eth0: initializing cpsw version 1.12 (0)
[   42.937224] net eth0: phy found : id is : 0x4dd076
[   42.957608] omap_hsmmc_resume
[   43.067149] PM: resume of devices complete after 218.241 msecs
[   43.085387] PM: Sending message for resetting M3 state machine
[   43.092981] Restarting tasks ... done.
[   43.177550] omap_hsmmc_runtime_resume
[   46.937556] libphy: 4a101000.mdio:04 - Link is Up - 100/Full
root@stream800:~#

So it seems, pm_runtime_resume is triggered by system resume.

>
> Kind regards
> Ulf Hansson
>
>> +
>> +       return ret;
>>  }
>>
>>  static int omap_hsmmc_runtime_resume(struct device *dev)
>>  {
>>         struct omap_hsmmc_host *host;
>> +       int ret = 0;
>> +       unsigned long flags;
>>
>>         host = platform_get_drvdata(to_platform_device(dev));
>>         omap_hsmmc_context_restore(host);
>>         dev_dbg(dev, "enabled\n");
>>
>> -       return 0;
>> +       spin_lock_irqsave(&host->irq_lock, flags);
>> +       host->flags &= ~HSMMC_RUNTIME_SUSPENDED;
>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>> +               if (host->wake_irq && (host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>> +                       disable_irq_nosync(host->wake_irq);
>> +                       host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>> +               }
>> +
>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>> +               OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
>> +               OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
>> +       }
>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>> +       return ret;
>>  }
>>
>>  static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
>> diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
>> index 2bf1b30..51e70cf 100644
>> --- a/include/linux/platform_data/mmc-omap.h
>> +++ b/include/linux/platform_data/mmc-omap.h
>> @@ -28,6 +28,7 @@
>>   */
>>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT          BIT(0)
>>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ      BIT(1)
>> +#define OMAP_HSMMC_SWAKEUP_MISSING             BIT(2)
>>
>>  struct mmc_card;
>>
>> --
>> 1.7.5.4
>>
>> --
>> 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] 28+ messages in thread

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-24 14:59         ` Andreas Fenkart
@ 2014-03-24 16:02           ` Ulf Hansson
  2014-03-24 16:34             ` Andreas Fenkart
  0 siblings, 1 reply; 28+ messages in thread
From: Ulf Hansson @ 2014-03-24 16:02 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Balaji T K, Tony Lindgren, linux-mmc, Chris Ball, linux-omap

On 24 March 2014 15:59, Andreas Fenkart <afenkart@gmail.com> wrote:
> Hi,
>
> 2014-03-24 13:43 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>> On 21 March 2014 17:17, Balaji T K <balajitk@ti.com> wrote:
>>> From: Andreas Fenkart <afenkart@gmail.com>
>>>
>>> There have been various patches floating around for enabling
>>> the SDIO IRQ for hsmmc, but none of them ever got merged.
>>>
>>> Probably the reason for not merging the SDIO interrupt patches
>>> has been the lack of wake-up path for SDIO on some omaps that
>>> has also needed remuxing the SDIO DAT1 line to a GPIO making
>>> the patches complex.
>>>
>>> This patch adds the minimal SDIO IRQ support to hsmmc for
>>> omaps that do have the wake-up path. For those omaps, the
>>> DAT1 line need to have the wake-up enable bit set, and the
>>> wake-up interrupt is the same as for the MMC controller.
>>>
>>> This patch has been tested on am3730 es1.2 with mwifiex
>>> connected to MMC3 with mwifiex waking to Ethernet traffic
>>> from off-idle mode. Note that for omaps that do not have
>>> the SDIO wake-up path, this patch will not work for idle
>>> modes and further patches for remuxing DAT1 to GPIO are
>>> needed.
>>>
>>> Based on earlier patches [1][2] by David Vrabel
>>> <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
>>> and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
>>> handing improved following how sdhci.c is doing it.
>>>
>>> For now, only support SDIO interrupt if we are booted with
>>> a separate wake-irq configued via device tree. This is
>>> because omaps need the wake-irq for idle states, and some
>>> omaps 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.
>>>
>>> To use it, you need to specify the wake-irq using the
>>> interrupts-extended property.
>>>
>>> [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
>>> [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446
>>>
>>> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>>> Signed-off-by: Tony Lindgren <tony@atomide.com>
>>> Signed-off-by: Balaji T K <balajitk@ti.com>
>>> ---
>>>  drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
>>>  include/linux/platform_data/mmc-omap.h |    1 +
>>>  2 files changed, 196 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>>> index 38a75bc..0275e0a 100644
>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>> @@ -29,6 +29,7 @@
>>>  #include <linux/timer.h>
>>>  #include <linux/clk.h>
>>>  #include <linux/of.h>
>>> +#include <linux/of_irq.h>
>>>  #include <linux/of_gpio.h>
>>>  #include <linux/of_device.h>
>>>  #include <linux/omap-dma.h>
>>> @@ -36,6 +37,7 @@
>>>  #include <linux/mmc/core.h>
>>>  #include <linux/mmc/mmc.h>
>>>  #include <linux/io.h>
>>> +#include <linux/irq.h>
>>>  #include <linux/gpio.h>
>>>  #include <linux/regulator/consumer.h>
>>>  #include <linux/pinctrl/consumer.h>
>>> @@ -106,6 +108,7 @@
>>>  #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)
>>> @@ -140,7 +143,6 @@
>>>  #define VDD_3V0                        3000000         /* 300000 uV */
>>>  #define VDD_165_195            (ffs(MMC_VDD_165_195) - 1)
>>>
>>> -#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>>  /*
>>>   * One controller can have multiple slots, like on some omap boards using
>>>   * omap.c controller driver. Luckily this is not currently done on any known
>>> @@ -194,6 +196,7 @@ struct omap_hsmmc_host {
>>>         u32                     sysctl;
>>>         u32                     capa;
>>>         int                     irq;
>>> +       int                     wake_irq;
>>>         int                     use_dma, dma_ch;
>>>         struct dma_chan         *tx_chan;
>>>         struct dma_chan         *rx_chan;
>>> @@ -206,6 +209,11 @@ struct omap_hsmmc_host {
>>>         int                     req_in_progress;
>>>         unsigned long           clk_rate;
>>>         unsigned int            flags;
>>> +#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
>>> +#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>
> merge conflict here? I do not use HSMMC_RUNTIME_SUSPEND anymore
> and of course, neither do I define AUTO_CMD23. :-)
>
>>> +#define HSMMC_SWAKEUP_QUIRK    (1 << 2)
>>> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 3)        /* SDIO irq enabled */
>>> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 4)
>>>         struct omap_hsmmc_next  next_data;
>>>         struct  omap_mmc_platform_data  *pdata;
>>>  };
>>> @@ -510,27 +518,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 MMC core */
>>> +       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 but need to keep 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 */
>>> @@ -1117,8 +1138,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);
>>> @@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>>>         return IRQ_HANDLED;
>>>  }
>>>
>>> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
>>> +{
>>> +       struct omap_hsmmc_host *host = dev_id;
>>> +       unsigned long flags;
>>> +
>>> +       /* cirq is level triggered, disable to avoid infinite loop */
>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>> +       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
>>> +               disable_irq_nosync(host->wake_irq);
>>> +               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>> +       }
>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>> +
>>> +       pm_request_resume(host->dev); /* no use counter */
>>> +
>>> +       return IRQ_HANDLED;
>>> +}
>>> +
>>>  static void set_sd_bus_power(struct omap_hsmmc_host *host)
>>>  {
>>>         unsigned long i;
>>> @@ -1638,6 +1681,81 @@ 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;
>>> +
>>> +       if (enable)
>>> +               pm_runtime_get_sync(host->dev);
>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>> +       if (host->flags & HSMMC_RUNTIME_SUSPENDED)
>>> +               goto out;
>>
>> This seems wrong to me.
>>
>> What if the host is suspended (runtime PM wise) and you want to
>> disable SDIO irq.
>> Won't SDIO irq be kept enabled in this case?
>
> Whenever you enter this function the module is not suspended,
> mind that register accesses will fail without fclk. So before entering here,
> there must have been a  pm_runtime_resume somewhere.

That is not correct I believe! You may very well have "disabled" host,
since there nobody that have claimed it, once you get an SDIO irq to
handle.

Additionally, if your statement stands, why do you need the
pm_runtime_get_sync when we are about to enable SDIO irq?


>
>>
>>> +
>>> +       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);
>>> +
>>> +out:
>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>> +       if (enable) {
>>> +               pm_runtime_mark_last_busy(host->dev);
>>> +               pm_runtime_put_autosuspend(host->dev);
>>> +       }
>>> +}
>>> +
>>> +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>>> +{
>>> +       struct mmc_host *mmc = host->mmc;
>>> +       int ret;
>>> +
>>> +       /*
>>> +        * For omaps with wake-up path, wakeirq will be irq from pinctrl and
>>> +        * for other omaps, wakeirq will be from GPIO (dat line remuxed to
>>> +        * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
>>> +        * with functional clock disabled.
>>> +        */
>>> +       if (!host->dev->of_node || !host->wake_irq)
>>> +               return -ENODEV;
>>> +
>>> +       /* Prevent auto-enabling of IRQ */
>>> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
>>> +       ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
>>> +                         IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>>> +                         mmc_hostname(mmc), host);
>>> +       if (ret) {
>>> +               dev_err(mmc_dev(host->mmc),
>>> +                       "Unable to request wake IRQ\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       /*
>>> +        * Some omaps don't have wake-up path from deeper idle states
>>> +        * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
>>> +        */
>>> +       if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
>>> +               host->flags |= HSMMC_SWAKEUP_QUIRK;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>>>  {
>>>         u32 hctl, capa, value;
>>> @@ -1690,7 +1808,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
>>> @@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
>>>         .reg_offset = 0x100,
>>>  };
>>>
>>> +static struct omap_mmc_of_data am33xx_mmc_of_data = {
>>> +       .reg_offset = 0x100,
>>> +       .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
>>> +};
>>> +
>>>  static const struct of_device_id omap_mmc_of_match[] = {
>>>         {
>>>                 .compatible = "ti,omap2-hsmmc",
>>> @@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
>>>                 .compatible = "ti,omap4-hsmmc",
>>>                 .data = &omap4_mmc_of_data,
>>>         },
>>> +       {
>>> +               .compatible = "ti,am33xx-hsmmc",
>>> +               .data = &am33xx_mmc_of_data,
>>> +       },
>>>         {},
>>>  };
>>>  MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
>>> @@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
>>>  {
>>>         return ERR_PTR(-EINVAL);
>>>  }
>>> +#define omap_mmc_of_match      NULL
>>>  #endif
>>>
>>>  static int omap_hsmmc_probe(struct platform_device *pdev)
>>> @@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>
>>>         platform_set_drvdata(pdev, host);
>>>
>>> +       if (pdev->dev.of_node)
>>> +               host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
>>> +
>>>         mmc->ops        = &omap_hsmmc_ops;
>>>
>>>         mmc->f_min = OMAP_MMC_MIN_CLOCK;
>>> @@ -2065,6 +2196,18 @@ 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 have a separate
>>> +        * wake-up interrupt configured from device tree. This is because
>>> +        * the wake-up interrupt is needed for idle state and 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.
>>> +        */
>>> +       ret = omap_hsmmc_configure_wake_irq(host);
>>> +       if (!ret)
>>> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
>>> +
>>>         omap_hsmmc_protect_card(host);
>>>
>>>         mmc_add_host(mmc);
>>> @@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>
>>>  err_slot_name:
>>>         mmc_remove_host(mmc);
>>> +       if (host->wake_irq)
>>> +               free_irq(host->wake_irq, host);
>>>  err_irq_cd:
>>>         if (host->use_reg)
>>>                 omap_hsmmc_reg_put(host);
>>>  err_reg:
>>>         if (host->pdata->cleanup)
>>>                 host->pdata->cleanup(&pdev->dev);
>>> +       if (host->wake_irq)
>>> +               free_irq(host->wake_irq, host);
>>>  err_irq:
>>>         if (host->tx_chan)
>>>                 dma_release_channel(host->tx_chan);
>>> @@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
>>>                                 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>>>         }
>>>
>>> +       if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>> +               disable_irq(host->wake_irq);
>>> +
>>>         if (host->dbclk)
>>>                 clk_disable_unprepare(host->dbclk);
>>>
>>> @@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
>>>
>>>         omap_hsmmc_protect_card(host);
>>>
>>> +       if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>> +               enable_irq(host->wake_irq);
>>> +
>>>         pm_runtime_mark_last_busy(host->dev);
>>>         pm_runtime_put_autosuspend(host->dev);
>>>         return 0;
>>> @@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
>>>  static int omap_hsmmc_runtime_suspend(struct device *dev)
>>>  {
>>>         struct omap_hsmmc_host *host;
>>> +       int ret = 0;
>>> +       unsigned long flags;
>>>
>>>         host = platform_get_drvdata(to_platform_device(dev));
>>>         omap_hsmmc_context_save(host);
>>>         dev_dbg(dev, "disabled\n");
>>>
>>> -       return 0;
>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>> +       host->flags |= HSMMC_RUNTIME_SUSPENDED;
>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
>>> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>> +               if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>> +                       enable_irq(host->wake_irq);
>>> +                       host->flags |= HSMMC_WAKE_IRQ_ENABLED;
>>> +               }
>>> +       }
>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>
>> This seems like code you need to execute during system suspend as
>> well? How will you else make sure the wakeup irq is fetched in this
>> scenario?
>>
>> Maybe, you have a work around for this problem in the omap power domain?
>
> I just added printks in resume and pm_runtime_resume, and did a
> suspend/resume cycle:
>
> [   42.853308] net eth0: initializing cpsw version 1.12 (0)
> [   42.937224] net eth0: phy found : id is : 0x4dd076
> [   42.957608] omap_hsmmc_resume
> [   43.067149] PM: resume of devices complete after 218.241 msecs
> [   43.085387] PM: Sending message for resetting M3 state machine
> [   43.092981] Restarting tasks ... done.
> [   43.177550] omap_hsmmc_runtime_resume
> [   46.937556] libphy: 4a101000.mdio:04 - Link is Up - 100/Full
> root@stream800:~#
>
> So it seems, pm_runtime_resume is triggered by system resume.

It seems that sometime _after_ system resume has completed, your
device will be runtime resumed. That not surprising, that will happen
sooner or later anyway.

But, the question is if device will be properly resumed to fetch the
SDIO irq, when it's triggered as wakeup.

>
>>
>> Kind regards
>> Ulf Hansson
>>
>>> +
>>> +       return ret;
>>>  }
>>>
>>>  static int omap_hsmmc_runtime_resume(struct device *dev)
>>>  {
>>>         struct omap_hsmmc_host *host;
>>> +       int ret = 0;
>>> +       unsigned long flags;
>>>
>>>         host = platform_get_drvdata(to_platform_device(dev));
>>>         omap_hsmmc_context_restore(host);
>>>         dev_dbg(dev, "enabled\n");
>>>
>>> -       return 0;
>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>> +       host->flags &= ~HSMMC_RUNTIME_SUSPENDED;
>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>> +               if (host->wake_irq && (host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>> +                       disable_irq_nosync(host->wake_irq);
>>> +                       host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>> +               }
>>> +
>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>> +               OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
>>> +               OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
>>> +       }
>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>> +       return ret;
>>>  }
>>>
>>>  static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
>>> diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
>>> index 2bf1b30..51e70cf 100644
>>> --- a/include/linux/platform_data/mmc-omap.h
>>> +++ b/include/linux/platform_data/mmc-omap.h
>>> @@ -28,6 +28,7 @@
>>>   */
>>>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT          BIT(0)
>>>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ      BIT(1)
>>> +#define OMAP_HSMMC_SWAKEUP_MISSING             BIT(2)
>>>
>>>  struct mmc_card;
>>>
>>> --
>>> 1.7.5.4
>>>
>>> --
>>> 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] 28+ messages in thread

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-24 16:02           ` Ulf Hansson
@ 2014-03-24 16:34             ` Andreas Fenkart
  2014-03-25  8:07               ` Ulf Hansson
  0 siblings, 1 reply; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-24 16:34 UTC (permalink / raw)
  To: Ulf Hansson; +Cc: Balaji T K, Tony Lindgren, linux-mmc, Chris Ball, linux-omap

2014-03-24 17:02 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
> On 24 March 2014 15:59, Andreas Fenkart <afenkart@gmail.com> wrote:
>> Hi,
>>
>> 2014-03-24 13:43 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>> On 21 March 2014 17:17, Balaji T K <balajitk@ti.com> wrote:
>>>> From: Andreas Fenkart <afenkart@gmail.com>
>>>>
>>>> There have been various patches floating around for enabling
>>>> the SDIO IRQ for hsmmc, but none of them ever got merged.
>>>>
>>>> Probably the reason for not merging the SDIO interrupt patches
>>>> has been the lack of wake-up path for SDIO on some omaps that
>>>> has also needed remuxing the SDIO DAT1 line to a GPIO making
>>>> the patches complex.
>>>>
>>>> This patch adds the minimal SDIO IRQ support to hsmmc for
>>>> omaps that do have the wake-up path. For those omaps, the
>>>> DAT1 line need to have the wake-up enable bit set, and the
>>>> wake-up interrupt is the same as for the MMC controller.
>>>>
>>>> This patch has been tested on am3730 es1.2 with mwifiex
>>>> connected to MMC3 with mwifiex waking to Ethernet traffic
>>>> from off-idle mode. Note that for omaps that do not have
>>>> the SDIO wake-up path, this patch will not work for idle
>>>> modes and further patches for remuxing DAT1 to GPIO are
>>>> needed.
>>>>
>>>> Based on earlier patches [1][2] by David Vrabel
>>>> <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
>>>> and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
>>>> handing improved following how sdhci.c is doing it.
>>>>
>>>> For now, only support SDIO interrupt if we are booted with
>>>> a separate wake-irq configued via device tree. This is
>>>> because omaps need the wake-irq for idle states, and some
>>>> omaps 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.
>>>>
>>>> To use it, you need to specify the wake-irq using the
>>>> interrupts-extended property.
>>>>
>>>> [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
>>>> [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446
>>>>
>>>> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>>>> Signed-off-by: Tony Lindgren <tony@atomide.com>
>>>> Signed-off-by: Balaji T K <balajitk@ti.com>
>>>> ---
>>>>  drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
>>>>  include/linux/platform_data/mmc-omap.h |    1 +
>>>>  2 files changed, 196 insertions(+), 12 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>>>> index 38a75bc..0275e0a 100644
>>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>>> @@ -29,6 +29,7 @@
>>>>  #include <linux/timer.h>
>>>>  #include <linux/clk.h>
>>>>  #include <linux/of.h>
>>>> +#include <linux/of_irq.h>
>>>>  #include <linux/of_gpio.h>
>>>>  #include <linux/of_device.h>
>>>>  #include <linux/omap-dma.h>
>>>> @@ -36,6 +37,7 @@
>>>>  #include <linux/mmc/core.h>
>>>>  #include <linux/mmc/mmc.h>
>>>>  #include <linux/io.h>
>>>> +#include <linux/irq.h>
>>>>  #include <linux/gpio.h>
>>>>  #include <linux/regulator/consumer.h>
>>>>  #include <linux/pinctrl/consumer.h>
>>>> @@ -106,6 +108,7 @@
>>>>  #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)
>>>> @@ -140,7 +143,6 @@
>>>>  #define VDD_3V0                        3000000         /* 300000 uV */
>>>>  #define VDD_165_195            (ffs(MMC_VDD_165_195) - 1)
>>>>
>>>> -#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>>>  /*
>>>>   * One controller can have multiple slots, like on some omap boards using
>>>>   * omap.c controller driver. Luckily this is not currently done on any known
>>>> @@ -194,6 +196,7 @@ struct omap_hsmmc_host {
>>>>         u32                     sysctl;
>>>>         u32                     capa;
>>>>         int                     irq;
>>>> +       int                     wake_irq;
>>>>         int                     use_dma, dma_ch;
>>>>         struct dma_chan         *tx_chan;
>>>>         struct dma_chan         *rx_chan;
>>>> @@ -206,6 +209,11 @@ struct omap_hsmmc_host {
>>>>         int                     req_in_progress;
>>>>         unsigned long           clk_rate;
>>>>         unsigned int            flags;
>>>> +#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
>>>> +#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>
>> merge conflict here? I do not use HSMMC_RUNTIME_SUSPEND anymore
>> and of course, neither do I define AUTO_CMD23. :-)
>>
>>>> +#define HSMMC_SWAKEUP_QUIRK    (1 << 2)
>>>> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 3)        /* SDIO irq enabled */
>>>> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 4)
>>>>         struct omap_hsmmc_next  next_data;
>>>>         struct  omap_mmc_platform_data  *pdata;
>>>>  };
>>>> @@ -510,27 +518,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 MMC core */
>>>> +       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 but need to keep 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 */
>>>> @@ -1117,8 +1138,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);
>>>> @@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>>>>         return IRQ_HANDLED;
>>>>  }
>>>>
>>>> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
>>>> +{
>>>> +       struct omap_hsmmc_host *host = dev_id;
>>>> +       unsigned long flags;
>>>> +
>>>> +       /* cirq is level triggered, disable to avoid infinite loop */
>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>> +       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
>>>> +               disable_irq_nosync(host->wake_irq);
>>>> +               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>>> +       }
>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>> +
>>>> +       pm_request_resume(host->dev); /* no use counter */
>>>> +
>>>> +       return IRQ_HANDLED;
>>>> +}
>>>> +
>>>>  static void set_sd_bus_power(struct omap_hsmmc_host *host)
>>>>  {
>>>>         unsigned long i;
>>>> @@ -1638,6 +1681,81 @@ 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;
>>>> +

/from here/

>>>> +       if (enable)
>>>> +               pm_runtime_get_sync(host->dev);
>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>> +       if (host->flags & HSMMC_RUNTIME_SUSPENDED)
>>>> +               goto out;

/till here/

This looks like a very old version of my patch, where did you got this
from? Could it be that you have some local merge conflict? See also my
comment above about the the AUTO_CMD23 comment.
Pls start over with the original patch of this series

>>>
>>> This seems wrong to me.
>>>
>>> What if the host is suspended (runtime PM wise) and you want to
>>> disable SDIO irq.
>>> Won't SDIO irq be kept enabled in this case?
>>
>> Whenever you enter this function the module is not suspended,
>> mind that register accesses will fail without fclk. So before entering here,
>> there must have been a  pm_runtime_resume somewhere.
>
> That is not correct I believe! You may very well have "disabled" host,
> since there nobody that have claimed it, once you get an SDIO irq to
> handle.
>
> Additionally, if your statement stands, why do you need the
> pm_runtime_get_sync when we are about to enable SDIO irq?
>
>
>>
>>>
>>>> +
>>>> +       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);
>>>> +
>>>> +out:
>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>> +       if (enable) {
>>>> +               pm_runtime_mark_last_busy(host->dev);
>>>> +               pm_runtime_put_autosuspend(host->dev);
>>>> +       }
>>>> +}
>>>> +
>>>> +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>>>> +{
>>>> +       struct mmc_host *mmc = host->mmc;
>>>> +       int ret;
>>>> +
>>>> +       /*
>>>> +        * For omaps with wake-up path, wakeirq will be irq from pinctrl and
>>>> +        * for other omaps, wakeirq will be from GPIO (dat line remuxed to
>>>> +        * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
>>>> +        * with functional clock disabled.
>>>> +        */
>>>> +       if (!host->dev->of_node || !host->wake_irq)
>>>> +               return -ENODEV;
>>>> +
>>>> +       /* Prevent auto-enabling of IRQ */
>>>> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
>>>> +       ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
>>>> +                         IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>>>> +                         mmc_hostname(mmc), host);
>>>> +       if (ret) {
>>>> +               dev_err(mmc_dev(host->mmc),
>>>> +                       "Unable to request wake IRQ\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * Some omaps don't have wake-up path from deeper idle states
>>>> +        * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
>>>> +        */
>>>> +       if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
>>>> +               host->flags |= HSMMC_SWAKEUP_QUIRK;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>>>>  {
>>>>         u32 hctl, capa, value;
>>>> @@ -1690,7 +1808,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
>>>> @@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
>>>>         .reg_offset = 0x100,
>>>>  };
>>>>
>>>> +static struct omap_mmc_of_data am33xx_mmc_of_data = {
>>>> +       .reg_offset = 0x100,
>>>> +       .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
>>>> +};
>>>> +
>>>>  static const struct of_device_id omap_mmc_of_match[] = {
>>>>         {
>>>>                 .compatible = "ti,omap2-hsmmc",
>>>> @@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
>>>>                 .compatible = "ti,omap4-hsmmc",
>>>>                 .data = &omap4_mmc_of_data,
>>>>         },
>>>> +       {
>>>> +               .compatible = "ti,am33xx-hsmmc",
>>>> +               .data = &am33xx_mmc_of_data,
>>>> +       },
>>>>         {},
>>>>  };
>>>>  MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
>>>> @@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
>>>>  {
>>>>         return ERR_PTR(-EINVAL);
>>>>  }
>>>> +#define omap_mmc_of_match      NULL
>>>>  #endif
>>>>
>>>>  static int omap_hsmmc_probe(struct platform_device *pdev)
>>>> @@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>
>>>>         platform_set_drvdata(pdev, host);
>>>>
>>>> +       if (pdev->dev.of_node)
>>>> +               host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
>>>> +
>>>>         mmc->ops        = &omap_hsmmc_ops;
>>>>
>>>>         mmc->f_min = OMAP_MMC_MIN_CLOCK;
>>>> @@ -2065,6 +2196,18 @@ 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 have a separate
>>>> +        * wake-up interrupt configured from device tree. This is because
>>>> +        * the wake-up interrupt is needed for idle state and 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.
>>>> +        */
>>>> +       ret = omap_hsmmc_configure_wake_irq(host);
>>>> +       if (!ret)
>>>> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
>>>> +
>>>>         omap_hsmmc_protect_card(host);
>>>>
>>>>         mmc_add_host(mmc);
>>>> @@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>
>>>>  err_slot_name:
>>>>         mmc_remove_host(mmc);
>>>> +       if (host->wake_irq)
>>>> +               free_irq(host->wake_irq, host);
>>>>  err_irq_cd:
>>>>         if (host->use_reg)
>>>>                 omap_hsmmc_reg_put(host);
>>>>  err_reg:
>>>>         if (host->pdata->cleanup)
>>>>                 host->pdata->cleanup(&pdev->dev);
>>>> +       if (host->wake_irq)
>>>> +               free_irq(host->wake_irq, host);
>>>>  err_irq:
>>>>         if (host->tx_chan)
>>>>                 dma_release_channel(host->tx_chan);
>>>> @@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
>>>>                                 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>>>>         }
>>>>
>>>> +       if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>>> +               disable_irq(host->wake_irq);
>>>> +
>>>>         if (host->dbclk)
>>>>                 clk_disable_unprepare(host->dbclk);
>>>>
>>>> @@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
>>>>
>>>>         omap_hsmmc_protect_card(host);
>>>>
>>>> +       if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>>> +               enable_irq(host->wake_irq);
>>>> +
>>>>         pm_runtime_mark_last_busy(host->dev);
>>>>         pm_runtime_put_autosuspend(host->dev);
>>>>         return 0;
>>>> @@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
>>>>  static int omap_hsmmc_runtime_suspend(struct device *dev)
>>>>  {
>>>>         struct omap_hsmmc_host *host;
>>>> +       int ret = 0;
>>>> +       unsigned long flags;
>>>>
>>>>         host = platform_get_drvdata(to_platform_device(dev));
>>>>         omap_hsmmc_context_save(host);
>>>>         dev_dbg(dev, "disabled\n");
>>>>
>>>> -       return 0;
>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>> +       host->flags |= HSMMC_RUNTIME_SUSPENDED;
>>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>>> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
>>>> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
>>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>>> +               if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>>> +                       enable_irq(host->wake_irq);
>>>> +                       host->flags |= HSMMC_WAKE_IRQ_ENABLED;
>>>> +               }
>>>> +       }
>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>
>>> This seems like code you need to execute during system suspend as
>>> well? How will you else make sure the wakeup irq is fetched in this
>>> scenario?
>>>
>>> Maybe, you have a work around for this problem in the omap power domain?
>>
>> I just added printks in resume and pm_runtime_resume, and did a
>> suspend/resume cycle:
>>
>> [   42.853308] net eth0: initializing cpsw version 1.12 (0)
>> [   42.937224] net eth0: phy found : id is : 0x4dd076
>> [   42.957608] omap_hsmmc_resume
>> [   43.067149] PM: resume of devices complete after 218.241 msecs
>> [   43.085387] PM: Sending message for resetting M3 state machine
>> [   43.092981] Restarting tasks ... done.
>> [   43.177550] omap_hsmmc_runtime_resume
>> [   46.937556] libphy: 4a101000.mdio:04 - Link is Up - 100/Full
>> root@stream800:~#
>>
>> So it seems, pm_runtime_resume is triggered by system resume.
>
> It seems that sometime _after_ system resume has completed, your
> device will be runtime resumed. That not surprising, that will happen
> sooner or later anyway.
>
> But, the question is if device will be properly resumed to fetch the
> SDIO irq, when it's triggered as wakeup.
>
>>
>>>
>>> Kind regards
>>> Ulf Hansson
>>>
>>>> +
>>>> +       return ret;
>>>>  }
>>>>
>>>>  static int omap_hsmmc_runtime_resume(struct device *dev)
>>>>  {
>>>>         struct omap_hsmmc_host *host;
>>>> +       int ret = 0;
>>>> +       unsigned long flags;
>>>>
>>>>         host = platform_get_drvdata(to_platform_device(dev));
>>>>         omap_hsmmc_context_restore(host);
>>>>         dev_dbg(dev, "enabled\n");
>>>>
>>>> -       return 0;
>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>> +       host->flags &= ~HSMMC_RUNTIME_SUSPENDED;
>>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>>> +               if (host->wake_irq && (host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>>> +                       disable_irq_nosync(host->wake_irq);
>>>> +                       host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>>> +               }
>>>> +
>>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>>> +               OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
>>>> +               OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
>>>> +       }
>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>> +       return ret;
>>>>  }
>>>>
>>>>  static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
>>>> diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
>>>> index 2bf1b30..51e70cf 100644
>>>> --- a/include/linux/platform_data/mmc-omap.h
>>>> +++ b/include/linux/platform_data/mmc-omap.h
>>>> @@ -28,6 +28,7 @@
>>>>   */
>>>>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT          BIT(0)
>>>>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ      BIT(1)
>>>> +#define OMAP_HSMMC_SWAKEUP_MISSING             BIT(2)
>>>>
>>>>  struct mmc_card;
>>>>
>>>> --
>>>> 1.7.5.4
>>>>
>>>> --
>>>> 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] 28+ messages in thread

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-24 16:34             ` Andreas Fenkart
@ 2014-03-25  8:07               ` Ulf Hansson
  2014-03-25 15:19                 ` Balaji T K
  0 siblings, 1 reply; 28+ messages in thread
From: Ulf Hansson @ 2014-03-25  8:07 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Balaji T K, Tony Lindgren, linux-mmc, Chris Ball, linux-omap

On 24 March 2014 17:34, Andreas Fenkart <afenkart@gmail.com> wrote:
> 2014-03-24 17:02 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>> On 24 March 2014 15:59, Andreas Fenkart <afenkart@gmail.com> wrote:
>>> Hi,
>>>
>>> 2014-03-24 13:43 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>>> On 21 March 2014 17:17, Balaji T K <balajitk@ti.com> wrote:
>>>>> From: Andreas Fenkart <afenkart@gmail.com>
>>>>>
>>>>> There have been various patches floating around for enabling
>>>>> the SDIO IRQ for hsmmc, but none of them ever got merged.
>>>>>
>>>>> Probably the reason for not merging the SDIO interrupt patches
>>>>> has been the lack of wake-up path for SDIO on some omaps that
>>>>> has also needed remuxing the SDIO DAT1 line to a GPIO making
>>>>> the patches complex.
>>>>>
>>>>> This patch adds the minimal SDIO IRQ support to hsmmc for
>>>>> omaps that do have the wake-up path. For those omaps, the
>>>>> DAT1 line need to have the wake-up enable bit set, and the
>>>>> wake-up interrupt is the same as for the MMC controller.
>>>>>
>>>>> This patch has been tested on am3730 es1.2 with mwifiex
>>>>> connected to MMC3 with mwifiex waking to Ethernet traffic
>>>>> from off-idle mode. Note that for omaps that do not have
>>>>> the SDIO wake-up path, this patch will not work for idle
>>>>> modes and further patches for remuxing DAT1 to GPIO are
>>>>> needed.
>>>>>
>>>>> Based on earlier patches [1][2] by David Vrabel
>>>>> <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
>>>>> and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
>>>>> handing improved following how sdhci.c is doing it.
>>>>>
>>>>> For now, only support SDIO interrupt if we are booted with
>>>>> a separate wake-irq configued via device tree. This is
>>>>> because omaps need the wake-irq for idle states, and some
>>>>> omaps 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.
>>>>>
>>>>> To use it, you need to specify the wake-irq using the
>>>>> interrupts-extended property.
>>>>>
>>>>> [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
>>>>> [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446
>>>>>
>>>>> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>>>>> Signed-off-by: Tony Lindgren <tony@atomide.com>
>>>>> Signed-off-by: Balaji T K <balajitk@ti.com>
>>>>> ---
>>>>>  drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
>>>>>  include/linux/platform_data/mmc-omap.h |    1 +
>>>>>  2 files changed, 196 insertions(+), 12 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>>>>> index 38a75bc..0275e0a 100644
>>>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>>>> @@ -29,6 +29,7 @@
>>>>>  #include <linux/timer.h>
>>>>>  #include <linux/clk.h>
>>>>>  #include <linux/of.h>
>>>>> +#include <linux/of_irq.h>
>>>>>  #include <linux/of_gpio.h>
>>>>>  #include <linux/of_device.h>
>>>>>  #include <linux/omap-dma.h>
>>>>> @@ -36,6 +37,7 @@
>>>>>  #include <linux/mmc/core.h>
>>>>>  #include <linux/mmc/mmc.h>
>>>>>  #include <linux/io.h>
>>>>> +#include <linux/irq.h>
>>>>>  #include <linux/gpio.h>
>>>>>  #include <linux/regulator/consumer.h>
>>>>>  #include <linux/pinctrl/consumer.h>
>>>>> @@ -106,6 +108,7 @@
>>>>>  #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)
>>>>> @@ -140,7 +143,6 @@
>>>>>  #define VDD_3V0                        3000000         /* 300000 uV */
>>>>>  #define VDD_165_195            (ffs(MMC_VDD_165_195) - 1)
>>>>>
>>>>> -#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>>>>  /*
>>>>>   * One controller can have multiple slots, like on some omap boards using
>>>>>   * omap.c controller driver. Luckily this is not currently done on any known
>>>>> @@ -194,6 +196,7 @@ struct omap_hsmmc_host {
>>>>>         u32                     sysctl;
>>>>>         u32                     capa;
>>>>>         int                     irq;
>>>>> +       int                     wake_irq;
>>>>>         int                     use_dma, dma_ch;
>>>>>         struct dma_chan         *tx_chan;
>>>>>         struct dma_chan         *rx_chan;
>>>>> @@ -206,6 +209,11 @@ struct omap_hsmmc_host {
>>>>>         int                     req_in_progress;
>>>>>         unsigned long           clk_rate;
>>>>>         unsigned int            flags;
>>>>> +#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
>>>>> +#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>>
>>> merge conflict here? I do not use HSMMC_RUNTIME_SUSPEND anymore
>>> and of course, neither do I define AUTO_CMD23. :-)
>>>
>>>>> +#define HSMMC_SWAKEUP_QUIRK    (1 << 2)
>>>>> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 3)        /* SDIO irq enabled */
>>>>> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 4)
>>>>>         struct omap_hsmmc_next  next_data;
>>>>>         struct  omap_mmc_platform_data  *pdata;
>>>>>  };
>>>>> @@ -510,27 +518,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 MMC core */
>>>>> +       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 but need to keep 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 */
>>>>> @@ -1117,8 +1138,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);
>>>>> @@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>>>>>         return IRQ_HANDLED;
>>>>>  }
>>>>>
>>>>> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
>>>>> +{
>>>>> +       struct omap_hsmmc_host *host = dev_id;
>>>>> +       unsigned long flags;
>>>>> +
>>>>> +       /* cirq is level triggered, disable to avoid infinite loop */
>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>> +       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
>>>>> +               disable_irq_nosync(host->wake_irq);
>>>>> +               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>>>> +       }
>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>> +
>>>>> +       pm_request_resume(host->dev); /* no use counter */
>>>>> +
>>>>> +       return IRQ_HANDLED;
>>>>> +}
>>>>> +
>>>>>  static void set_sd_bus_power(struct omap_hsmmc_host *host)
>>>>>  {
>>>>>         unsigned long i;
>>>>> @@ -1638,6 +1681,81 @@ 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;
>>>>> +
>
> /from here/
>
>>>>> +       if (enable)
>>>>> +               pm_runtime_get_sync(host->dev);
>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>> +       if (host->flags & HSMMC_RUNTIME_SUSPENDED)
>>>>> +               goto out;
>
> /till here/
>
> This looks like a very old version of my patch, where did you got this
> from? Could it be that you have some local merge conflict? See also my
> comment above about the the AUTO_CMD23 comment.
> Pls start over with the original patch of this series
>

It seems like I have been reviewing the patch Balaji sent 21 march,
with you as author. Apparently  Balaji must be using an old version of
your patch!? That's not good, especially if he is doing some tests as
well. :-)

[PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt


Anyway, when I get some time I will have a look at your original
patch. The latest should be this one right?
[PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt

Kind regards
Ulf Hanssson

>>>>
>>>> This seems wrong to me.
>>>>
>>>> What if the host is suspended (runtime PM wise) and you want to
>>>> disable SDIO irq.
>>>> Won't SDIO irq be kept enabled in this case?
>>>
>>> Whenever you enter this function the module is not suspended,
>>> mind that register accesses will fail without fclk. So before entering here,
>>> there must have been a  pm_runtime_resume somewhere.
>>
>> That is not correct I believe! You may very well have "disabled" host,
>> since there nobody that have claimed it, once you get an SDIO irq to
>> handle.
>>
>> Additionally, if your statement stands, why do you need the
>> pm_runtime_get_sync when we are about to enable SDIO irq?
>>
>>
>>>
>>>>
>>>>> +
>>>>> +       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);
>>>>> +
>>>>> +out:
>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>> +       if (enable) {
>>>>> +               pm_runtime_mark_last_busy(host->dev);
>>>>> +               pm_runtime_put_autosuspend(host->dev);
>>>>> +       }
>>>>> +}
>>>>> +
>>>>> +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>>>>> +{
>>>>> +       struct mmc_host *mmc = host->mmc;
>>>>> +       int ret;
>>>>> +
>>>>> +       /*
>>>>> +        * For omaps with wake-up path, wakeirq will be irq from pinctrl and
>>>>> +        * for other omaps, wakeirq will be from GPIO (dat line remuxed to
>>>>> +        * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
>>>>> +        * with functional clock disabled.
>>>>> +        */
>>>>> +       if (!host->dev->of_node || !host->wake_irq)
>>>>> +               return -ENODEV;
>>>>> +
>>>>> +       /* Prevent auto-enabling of IRQ */
>>>>> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
>>>>> +       ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
>>>>> +                         IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>>>>> +                         mmc_hostname(mmc), host);
>>>>> +       if (ret) {
>>>>> +               dev_err(mmc_dev(host->mmc),
>>>>> +                       "Unable to request wake IRQ\n");
>>>>> +               return ret;
>>>>> +       }
>>>>> +
>>>>> +       /*
>>>>> +        * Some omaps don't have wake-up path from deeper idle states
>>>>> +        * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
>>>>> +        */
>>>>> +       if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
>>>>> +               host->flags |= HSMMC_SWAKEUP_QUIRK;
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>>>>>  {
>>>>>         u32 hctl, capa, value;
>>>>> @@ -1690,7 +1808,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
>>>>> @@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
>>>>>         .reg_offset = 0x100,
>>>>>  };
>>>>>
>>>>> +static struct omap_mmc_of_data am33xx_mmc_of_data = {
>>>>> +       .reg_offset = 0x100,
>>>>> +       .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
>>>>> +};
>>>>> +
>>>>>  static const struct of_device_id omap_mmc_of_match[] = {
>>>>>         {
>>>>>                 .compatible = "ti,omap2-hsmmc",
>>>>> @@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
>>>>>                 .compatible = "ti,omap4-hsmmc",
>>>>>                 .data = &omap4_mmc_of_data,
>>>>>         },
>>>>> +       {
>>>>> +               .compatible = "ti,am33xx-hsmmc",
>>>>> +               .data = &am33xx_mmc_of_data,
>>>>> +       },
>>>>>         {},
>>>>>  };
>>>>>  MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
>>>>> @@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
>>>>>  {
>>>>>         return ERR_PTR(-EINVAL);
>>>>>  }
>>>>> +#define omap_mmc_of_match      NULL
>>>>>  #endif
>>>>>
>>>>>  static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>> @@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>>
>>>>>         platform_set_drvdata(pdev, host);
>>>>>
>>>>> +       if (pdev->dev.of_node)
>>>>> +               host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
>>>>> +
>>>>>         mmc->ops        = &omap_hsmmc_ops;
>>>>>
>>>>>         mmc->f_min = OMAP_MMC_MIN_CLOCK;
>>>>> @@ -2065,6 +2196,18 @@ 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 have a separate
>>>>> +        * wake-up interrupt configured from device tree. This is because
>>>>> +        * the wake-up interrupt is needed for idle state and 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.
>>>>> +        */
>>>>> +       ret = omap_hsmmc_configure_wake_irq(host);
>>>>> +       if (!ret)
>>>>> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
>>>>> +
>>>>>         omap_hsmmc_protect_card(host);
>>>>>
>>>>>         mmc_add_host(mmc);
>>>>> @@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>>
>>>>>  err_slot_name:
>>>>>         mmc_remove_host(mmc);
>>>>> +       if (host->wake_irq)
>>>>> +               free_irq(host->wake_irq, host);
>>>>>  err_irq_cd:
>>>>>         if (host->use_reg)
>>>>>                 omap_hsmmc_reg_put(host);
>>>>>  err_reg:
>>>>>         if (host->pdata->cleanup)
>>>>>                 host->pdata->cleanup(&pdev->dev);
>>>>> +       if (host->wake_irq)
>>>>> +               free_irq(host->wake_irq, host);
>>>>>  err_irq:
>>>>>         if (host->tx_chan)
>>>>>                 dma_release_channel(host->tx_chan);
>>>>> @@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
>>>>>                                 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>>>>>         }
>>>>>
>>>>> +       if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>>>> +               disable_irq(host->wake_irq);
>>>>> +
>>>>>         if (host->dbclk)
>>>>>                 clk_disable_unprepare(host->dbclk);
>>>>>
>>>>> @@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
>>>>>
>>>>>         omap_hsmmc_protect_card(host);
>>>>>
>>>>> +       if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>>>> +               enable_irq(host->wake_irq);
>>>>> +
>>>>>         pm_runtime_mark_last_busy(host->dev);
>>>>>         pm_runtime_put_autosuspend(host->dev);
>>>>>         return 0;
>>>>> @@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
>>>>>  static int omap_hsmmc_runtime_suspend(struct device *dev)
>>>>>  {
>>>>>         struct omap_hsmmc_host *host;
>>>>> +       int ret = 0;
>>>>> +       unsigned long flags;
>>>>>
>>>>>         host = platform_get_drvdata(to_platform_device(dev));
>>>>>         omap_hsmmc_context_save(host);
>>>>>         dev_dbg(dev, "disabled\n");
>>>>>
>>>>> -       return 0;
>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>> +       host->flags |= HSMMC_RUNTIME_SUSPENDED;
>>>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>>>> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
>>>>> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
>>>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>>>> +               if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>>>> +                       enable_irq(host->wake_irq);
>>>>> +                       host->flags |= HSMMC_WAKE_IRQ_ENABLED;
>>>>> +               }
>>>>> +       }
>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>
>>>> This seems like code you need to execute during system suspend as
>>>> well? How will you else make sure the wakeup irq is fetched in this
>>>> scenario?
>>>>
>>>> Maybe, you have a work around for this problem in the omap power domain?
>>>
>>> I just added printks in resume and pm_runtime_resume, and did a
>>> suspend/resume cycle:
>>>
>>> [   42.853308] net eth0: initializing cpsw version 1.12 (0)
>>> [   42.937224] net eth0: phy found : id is : 0x4dd076
>>> [   42.957608] omap_hsmmc_resume
>>> [   43.067149] PM: resume of devices complete after 218.241 msecs
>>> [   43.085387] PM: Sending message for resetting M3 state machine
>>> [   43.092981] Restarting tasks ... done.
>>> [   43.177550] omap_hsmmc_runtime_resume
>>> [   46.937556] libphy: 4a101000.mdio:04 - Link is Up - 100/Full
>>> root@stream800:~#
>>>
>>> So it seems, pm_runtime_resume is triggered by system resume.
>>
>> It seems that sometime _after_ system resume has completed, your
>> device will be runtime resumed. That not surprising, that will happen
>> sooner or later anyway.
>>
>> But, the question is if device will be properly resumed to fetch the
>> SDIO irq, when it's triggered as wakeup.
>>
>>>
>>>>
>>>> Kind regards
>>>> Ulf Hansson
>>>>
>>>>> +
>>>>> +       return ret;
>>>>>  }
>>>>>
>>>>>  static int omap_hsmmc_runtime_resume(struct device *dev)
>>>>>  {
>>>>>         struct omap_hsmmc_host *host;
>>>>> +       int ret = 0;
>>>>> +       unsigned long flags;
>>>>>
>>>>>         host = platform_get_drvdata(to_platform_device(dev));
>>>>>         omap_hsmmc_context_restore(host);
>>>>>         dev_dbg(dev, "enabled\n");
>>>>>
>>>>> -       return 0;
>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>> +       host->flags &= ~HSMMC_RUNTIME_SUSPENDED;
>>>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>>>> +               if (host->wake_irq && (host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>>>> +                       disable_irq_nosync(host->wake_irq);
>>>>> +                       host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>>>> +               }
>>>>> +
>>>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>>>> +               OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
>>>>> +               OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
>>>>> +       }
>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>> +       return ret;
>>>>>  }
>>>>>
>>>>>  static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
>>>>> diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
>>>>> index 2bf1b30..51e70cf 100644
>>>>> --- a/include/linux/platform_data/mmc-omap.h
>>>>> +++ b/include/linux/platform_data/mmc-omap.h
>>>>> @@ -28,6 +28,7 @@
>>>>>   */
>>>>>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT          BIT(0)
>>>>>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ      BIT(1)
>>>>> +#define OMAP_HSMMC_SWAKEUP_MISSING             BIT(2)
>>>>>
>>>>>  struct mmc_card;
>>>>>
>>>>> --
>>>>> 1.7.5.4
>>>>>
>>>>> --
>>>>> 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] 28+ messages in thread

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-25  8:07               ` Ulf Hansson
@ 2014-03-25 15:19                 ` Balaji T K
  2014-03-30 22:43                   ` Andreas Fenkart
  0 siblings, 1 reply; 28+ messages in thread
From: Balaji T K @ 2014-03-25 15:19 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Andreas Fenkart, Tony Lindgren, linux-mmc, Chris Ball, linux-omap

On Tuesday 25 March 2014 01:37 PM, Ulf Hansson wrote:
> On 24 March 2014 17:34, Andreas Fenkart <afenkart@gmail.com> wrote:
>> 2014-03-24 17:02 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>> On 24 March 2014 15:59, Andreas Fenkart <afenkart@gmail.com> wrote:
>>>> Hi,
>>>>
>>>> 2014-03-24 13:43 GMT+01:00 Ulf Hansson <ulf.hansson@linaro.org>:
>>>>> On 21 March 2014 17:17, Balaji T K <balajitk@ti.com> wrote:
>>>>>> From: Andreas Fenkart <afenkart@gmail.com>
>>>>>>
>>>>>> There have been various patches floating around for enabling
>>>>>> the SDIO IRQ for hsmmc, but none of them ever got merged.
>>>>>>
>>>>>> Probably the reason for not merging the SDIO interrupt patches
>>>>>> has been the lack of wake-up path for SDIO on some omaps that
>>>>>> has also needed remuxing the SDIO DAT1 line to a GPIO making
>>>>>> the patches complex.
>>>>>>
>>>>>> This patch adds the minimal SDIO IRQ support to hsmmc for
>>>>>> omaps that do have the wake-up path. For those omaps, the
>>>>>> DAT1 line need to have the wake-up enable bit set, and the
>>>>>> wake-up interrupt is the same as for the MMC controller.
>>>>>>
>>>>>> This patch has been tested on am3730 es1.2 with mwifiex
>>>>>> connected to MMC3 with mwifiex waking to Ethernet traffic
>>>>>> from off-idle mode. Note that for omaps that do not have
>>>>>> the SDIO wake-up path, this patch will not work for idle
>>>>>> modes and further patches for remuxing DAT1 to GPIO are
>>>>>> needed.
>>>>>>
>>>>>> Based on earlier patches [1][2] by David Vrabel
>>>>>> <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com>
>>>>>> and Andreas Fenkart <afenkart@gmail.com> with the SDIO IRQ
>>>>>> handing improved following how sdhci.c is doing it.
>>>>>>
>>>>>> For now, only support SDIO interrupt if we are booted with
>>>>>> a separate wake-irq configued via device tree. This is
>>>>>> because omaps need the wake-irq for idle states, and some
>>>>>> omaps 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.
>>>>>>
>>>>>> To use it, you need to specify the wake-irq using the
>>>>>> interrupts-extended property.
>>>>>>
>>>>>> [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453
>>>>>> [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446
>>>>>>
>>>>>> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>>>>>> Signed-off-by: Tony Lindgren <tony@atomide.com>
>>>>>> Signed-off-by: Balaji T K <balajitk@ti.com>
>>>>>> ---
>>>>>>   drivers/mmc/host/omap_hsmmc.c          |  207 ++++++++++++++++++++++++++++++--
>>>>>>   include/linux/platform_data/mmc-omap.h |    1 +
>>>>>>   2 files changed, 196 insertions(+), 12 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>>>>>> index 38a75bc..0275e0a 100644
>>>>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>>>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>>>>> @@ -29,6 +29,7 @@
>>>>>>   #include <linux/timer.h>
>>>>>>   #include <linux/clk.h>
>>>>>>   #include <linux/of.h>
>>>>>> +#include <linux/of_irq.h>
>>>>>>   #include <linux/of_gpio.h>
>>>>>>   #include <linux/of_device.h>
>>>>>>   #include <linux/omap-dma.h>
>>>>>> @@ -36,6 +37,7 @@
>>>>>>   #include <linux/mmc/core.h>
>>>>>>   #include <linux/mmc/mmc.h>
>>>>>>   #include <linux/io.h>
>>>>>> +#include <linux/irq.h>
>>>>>>   #include <linux/gpio.h>
>>>>>>   #include <linux/regulator/consumer.h>
>>>>>>   #include <linux/pinctrl/consumer.h>
>>>>>> @@ -106,6 +108,7 @@
>>>>>>   #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)
>>>>>> @@ -140,7 +143,6 @@
>>>>>>   #define VDD_3V0                        3000000         /* 300000 uV */
>>>>>>   #define VDD_165_195            (ffs(MMC_VDD_165_195) - 1)
>>>>>>
>>>>>> -#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */

Previous definition of AUTO_CMD23.

>>>>>>   /*
>>>>>>    * One controller can have multiple slots, like on some omap boards using
>>>>>>    * omap.c controller driver. Luckily this is not currently done on any known
>>>>>> @@ -194,6 +196,7 @@ struct omap_hsmmc_host {
>>>>>>          u32                     sysctl;
>>>>>>          u32                     capa;
>>>>>>          int                     irq;
>>>>>> +       int                     wake_irq;
>>>>>>          int                     use_dma, dma_ch;
>>>>>>          struct dma_chan         *tx_chan;
>>>>>>          struct dma_chan         *rx_chan;
>>>>>> @@ -206,6 +209,11 @@ struct omap_hsmmc_host {
>>>>>>          int                     req_in_progress;
>>>>>>          unsigned long           clk_rate;
>>>>>>          unsigned int            flags;
>>>>>> +#define HSMMC_RUNTIME_SUSPENDED (1 << 0)
>>>>>> +#define AUTO_CMD23             (1 << 1)        /* Auto CMD23 support */
>>>>
>>>> merge conflict here? I do not use HSMMC_RUNTIME_SUSPEND anymore
>>>> and of course, neither do I define AUTO_CMD23. :-)
>>>>

moved AUTO_CMD23 here to avoid overlap /redefinition while re-basing to mmc-next.

>>>>>> +#define HSMMC_SWAKEUP_QUIRK    (1 << 2)
>>>>>> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 3)        /* SDIO irq enabled */
>>>>>> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 4)
>>>>>>          struct omap_hsmmc_next  next_data;
>>>>>>          struct  omap_mmc_platform_data  *pdata;
>>>>>>   };
>>>>>> @@ -510,27 +518,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 MMC core */
>>>>>> +       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 but need to keep 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 */
>>>>>> @@ -1117,8 +1138,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);
>>>>>> @@ -1127,6 +1152,24 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>>>>>>          return IRQ_HANDLED;
>>>>>>   }
>>>>>>
>>>>>> +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
>>>>>> +{
>>>>>> +       struct omap_hsmmc_host *host = dev_id;
>>>>>> +       unsigned long flags;
>>>>>> +
>>>>>> +       /* cirq is level triggered, disable to avoid infinite loop */
>>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>>> +       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
>>>>>> +               disable_irq_nosync(host->wake_irq);
>>>>>> +               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
>>>>>> +       }
>>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>>> +
>>>>>> +       pm_request_resume(host->dev); /* no use counter */
>>>>>> +
>>>>>> +       return IRQ_HANDLED;
>>>>>> +}
>>>>>> +
>>>>>>   static void set_sd_bus_power(struct omap_hsmmc_host *host)
>>>>>>   {
>>>>>>          unsigned long i;
>>>>>> @@ -1638,6 +1681,81 @@ 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;
>>>>>> +
>>
>> /from here/
>>
>>>>>> +       if (enable)
>>>>>> +               pm_runtime_get_sync(host->dev);
>>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>>> +       if (host->flags & HSMMC_RUNTIME_SUSPENDED)
>>>>>> +               goto out;
>>
>> /till here/
>>
>> This looks like a very old version of my patch, where did you got this
>> from?

I added HSMMC_RUNTIME_SUSPEND, Got inspired from sdhci implementation!
and HSMMC_RUNTIME_SUSPENDED is needed for multi-processor system.

 >> Could it be that you have some local merge conflict? See also my
>> comment above about the the AUTO_CMD23 comment.
>> Pls start over with the original patch of this series
>>
>
> It seems like I have been reviewing the patch Balaji sent 21 march,
> with you as author. Apparently  Balaji must be using an old version of
> your patch!? That's not good, especially if he is doing some tests as
> well. :-)
>

No, It is not some old version :-)
This series is based on [PATCH v9 1/3] sent by Andreas[1]
+ few modification/cleanup by me[2] + re-base to mmc-next[3]

[1] http://www.spinics.net/lists/linux-omap/msg104252.html
[2] http://www.spinics.net/lists/linux-mmc/msg25521.html
[3] http://git.kernel.org/cgit/linux/kernel/git/cjb/mmc.git/log/?id=refs/heads/mmc-next

> [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
>
>
> Anyway, when I get some time I will have a look at your original
> patch. The latest should be this one right?
> [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt
>
> Kind regards
> Ulf Hanssson
>
>>>>>
>>>>> This seems wrong to me.
>>>>>
>>>>> What if the host is suspended (runtime PM wise) and you want to
>>>>> disable SDIO irq.
>>>>> Won't SDIO irq be kept enabled in this case?

First of all, In runtime suspended state is a corner case where sdio-irq
got triggered while .runtime_suspend hook was getting executed
in multi-processor system.

Since after runtime suspended, registers cannot be accessed
after clocks are disabled, So, sdio-irq will remain enabled.
sdio-irq will get re-triggered via (remuxed-)gpio or pinctrl wakeup
and sdio-irq will get disabled eventually on host side.

>>>>
>>>> Whenever you enter this function the module is not suspended,
>>>> mind that register accesses will fail without fclk. So before entering here,
>>>> there must have been a  pm_runtime_resume somewhere.

That is true for sdio-irq triggered after runtime suspend where pm_runtime_resume
is invoked from wake_irq handler but for other cases, sdio-irq can get triggered
during auto-suspend delay.

>>>
>>> That is not correct I believe! You may very well have "disabled" host,
>>> since there nobody that have claimed it, once you get an SDIO irq to
>>> handle.

Right, SDIO-irq are asynchronous to runtime_pm and can get irq event anytime.

>>>
>>> Additionally, if your statement stands, why do you need the
>>> pm_runtime_get_sync when we are about to enable SDIO irq?
>>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +       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);
>>>>>> +
>>>>>> +out:
>>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>>> +       if (enable) {
>>>>>> +               pm_runtime_mark_last_busy(host->dev);
>>>>>> +               pm_runtime_put_autosuspend(host->dev);
>>>>>> +       }
>>>>>> +}
>>>>>> +
>>>>>> +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>>>>>> +{
>>>>>> +       struct mmc_host *mmc = host->mmc;
>>>>>> +       int ret;
>>>>>> +
>>>>>> +       /*
>>>>>> +        * For omaps with wake-up path, wakeirq will be irq from pinctrl and
>>>>>> +        * for other omaps, wakeirq will be from GPIO (dat line remuxed to
>>>>>> +        * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
>>>>>> +        * with functional clock disabled.
>>>>>> +        */
>>>>>> +       if (!host->dev->of_node || !host->wake_irq)
>>>>>> +               return -ENODEV;
>>>>>> +
>>>>>> +       /* Prevent auto-enabling of IRQ */
>>>>>> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
>>>>>> +       ret = request_irq(host->wake_irq, omap_hsmmc_wake_irq,
>>>>>> +                         IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>>>>>> +                         mmc_hostname(mmc), host);
>>>>>> +       if (ret) {
>>>>>> +               dev_err(mmc_dev(host->mmc),
>>>>>> +                       "Unable to request wake IRQ\n");
>>>>>> +               return ret;
>>>>>> +       }
>>>>>> +
>>>>>> +       /*
>>>>>> +        * Some omaps don't have wake-up path from deeper idle states
>>>>>> +        * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
>>>>>> +        */
>>>>>> +       if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING)
>>>>>> +               host->flags |= HSMMC_SWAKEUP_QUIRK;
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>>   static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>>>>>>   {
>>>>>>          u32 hctl, capa, value;
>>>>>> @@ -1690,7 +1808,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
>>>>>> @@ -1761,6 +1879,11 @@ static const struct omap_mmc_of_data omap4_mmc_of_data = {
>>>>>>          .reg_offset = 0x100,
>>>>>>   };
>>>>>>
>>>>>> +static struct omap_mmc_of_data am33xx_mmc_of_data = {
>>>>>> +       .reg_offset = 0x100,
>>>>>> +       .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
>>>>>> +};
>>>>>> +
>>>>>>   static const struct of_device_id omap_mmc_of_match[] = {
>>>>>>          {
>>>>>>                  .compatible = "ti,omap2-hsmmc",
>>>>>> @@ -1776,6 +1899,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
>>>>>>                  .compatible = "ti,omap4-hsmmc",
>>>>>>                  .data = &omap4_mmc_of_data,
>>>>>>          },
>>>>>> +       {
>>>>>> +               .compatible = "ti,am33xx-hsmmc",
>>>>>> +               .data = &am33xx_mmc_of_data,
>>>>>> +       },
>>>>>>          {},
>>>>>>   };
>>>>>>   MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
>>>>>> @@ -1837,6 +1964,7 @@ static inline struct omap_mmc_platform_data
>>>>>>   {
>>>>>>          return ERR_PTR(-EINVAL);
>>>>>>   }
>>>>>> +#define omap_mmc_of_match      NULL
>>>>>>   #endif
>>>>>>
>>>>>>   static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>>> @@ -1912,6 +2040,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>>>
>>>>>>          platform_set_drvdata(pdev, host);
>>>>>>
>>>>>> +       if (pdev->dev.of_node)
>>>>>> +               host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
>>>>>> +
>>>>>>          mmc->ops        = &omap_hsmmc_ops;
>>>>>>
>>>>>>          mmc->f_min = OMAP_MMC_MIN_CLOCK;
>>>>>> @@ -2065,6 +2196,18 @@ 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 have a separate
>>>>>> +        * wake-up interrupt configured from device tree. This is because
>>>>>> +        * the wake-up interrupt is needed for idle state and 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.
>>>>>> +        */
>>>>>> +       ret = omap_hsmmc_configure_wake_irq(host);
>>>>>> +       if (!ret)
>>>>>> +               mmc->caps |= MMC_CAP_SDIO_IRQ;
>>>>>> +
>>>>>>          omap_hsmmc_protect_card(host);
>>>>>>
>>>>>>          mmc_add_host(mmc);
>>>>>> @@ -2089,12 +2232,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>>>>>>
>>>>>>   err_slot_name:
>>>>>>          mmc_remove_host(mmc);
>>>>>> +       if (host->wake_irq)
>>>>>> +               free_irq(host->wake_irq, host);
>>>>>>   err_irq_cd:
>>>>>>          if (host->use_reg)
>>>>>>                  omap_hsmmc_reg_put(host);
>>>>>>   err_reg:
>>>>>>          if (host->pdata->cleanup)
>>>>>>                  host->pdata->cleanup(&pdev->dev);
>>>>>> +       if (host->wake_irq)
>>>>>> +               free_irq(host->wake_irq, host);
>>>>>>   err_irq:
>>>>>>          if (host->tx_chan)
>>>>>>                  dma_release_channel(host->tx_chan);
>>>>>> @@ -2174,6 +2321,9 @@ static int omap_hsmmc_suspend(struct device *dev)
>>>>>>                                  OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>>>>>>          }
>>>>>>
>>>>>> +       if (host->wake_irq && !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>>>>> +               disable_irq(host->wake_irq);
>>>>>> +
>>>>>>          if (host->dbclk)
>>>>>>                  clk_disable_unprepare(host->dbclk);
>>>>>>
>>>>>> @@ -2199,6 +2349,9 @@ static int omap_hsmmc_resume(struct device *dev)
>>>>>>
>>>>>>          omap_hsmmc_protect_card(host);
>>>>>>
>>>>>> +       if (host->wake_irq & !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
>>>>>> +               enable_irq(host->wake_irq);
>>>>>> +
>>>>>>          pm_runtime_mark_last_busy(host->dev);
>>>>>>          pm_runtime_put_autosuspend(host->dev);
>>>>>>          return 0;
>>>>>> @@ -2214,23 +2367,53 @@ static int omap_hsmmc_resume(struct device *dev)
>>>>>>   static int omap_hsmmc_runtime_suspend(struct device *dev)
>>>>>>   {
>>>>>>          struct omap_hsmmc_host *host;
>>>>>> +       int ret = 0;
>>>>>> +       unsigned long flags;
>>>>>>
>>>>>>          host = platform_get_drvdata(to_platform_device(dev));
>>>>>>          omap_hsmmc_context_save(host);
>>>>>>          dev_dbg(dev, "disabled\n");
>>>>>>
>>>>>> -       return 0;
>>>>>> +       spin_lock_irqsave(&host->irq_lock, flags);
>>>>>> +       host->flags |= HSMMC_RUNTIME_SUSPENDED;
>>>>>> +       if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
>>>>>> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
>>>>>> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
>>>>>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>>>>> +               if (host->wake_irq && !(host->flags & HSMMC_WAKE_IRQ_ENABLED)) {
>>>>>> +                       enable_irq(host->wake_irq);
>>>>>> +                       host->flags |= HSMMC_WAKE_IRQ_ENABLED;
>>>>>> +               }
>>>>>> +       }
>>>>>> +       spin_unlock_irqrestore(&host->irq_lock, flags);
>>>>>
>>>>> This seems like code you need to execute during system suspend as
>>>>> well? How will you else make sure the wakeup irq is fetched in this
>>>>> scenario?
>>>>>
>>>>> Maybe, you have a work around for this problem in the omap power domain?
>>>>
>>>> I just added printks in resume and pm_runtime_resume, and did a
>>>> suspend/resume cycle:
>>>>
>>>> [   42.853308] net eth0: initializing cpsw version 1.12 (0)
>>>> [   42.937224] net eth0: phy found : id is : 0x4dd076
>>>> [   42.957608] omap_hsmmc_resume
>>>> [   43.067149] PM: resume of devices complete after 218.241 msecs
>>>> [   43.085387] PM: Sending message for resetting M3 state machine
>>>> [   43.092981] Restarting tasks ... done.
>>>> [   43.177550] omap_hsmmc_runtime_resume
>>>> [   46.937556] libphy: 4a101000.mdio:04 - Link is Up - 100/Full
>>>> root@stream800:~#
>>>>
>>>> So it seems, pm_runtime_resume is triggered by system resume.
>>>
>>> It seems that sometime _after_ system resume has completed, your
>>> device will be runtime resumed. That not surprising, that will happen
>>> sooner or later anyway.
>>>
>>> But, the question is if device will be properly resumed to fetch the
>>> SDIO irq, when it's triggered as wakeup.

Same way as it would happen in runtime suspend state. dat1 line being held
low, dat1 pad muxed to gpio, gpio triggers irq event and on pm_request_resume
in wakeup_irq handler. In runtime_resume function, with clock on, pad reassigned
back to dat1, irq registers set for enable sdio-irq, SDIO irq will be triggered
and signaled

Thanks and Regards,
Balaji T K

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

* Re: [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-25 15:19                 ` Balaji T K
@ 2014-03-30 22:43                   ` Andreas Fenkart
  0 siblings, 0 replies; 28+ messages in thread
From: Andreas Fenkart @ 2014-03-30 22:43 UTC (permalink / raw)
  To: Balaji T K; +Cc: Ulf Hansson, Tony Lindgren, linux-mmc, Chris Ball, linux-omap

Sorry missed your previous mail

2014-03-25 16:19 GMT+01:00 Balaji T K <balajitk@ti.com>:
>>>
>>>>>>
>>>>>> This seems wrong to me.
>>>>>>
>>>>>> What if the host is suspended (runtime PM wise) and you want to
>>>>>> disable SDIO irq.
>>>>>> Won't SDIO irq be kept enabled in this case?
>
>
> First of all, In runtime suspended state is a corner case where sdio-irq
> got triggered while .runtime_suspend hook was getting executed
> in multi-processor system.

Just to make sure I got you right

2 cores,

1. device was idle for xx ms, transition to runtime suspend
   initiated, host spinlock is aquired
2. meanwhile an sdio-irq is detected but not yet served
3. 2nd core starts process mmc irq, decodes it as an sdio_irq, but
   not yet processed due to spinlock held by runtime_suspend
4. device irqs are masked, clocks are gated, no more register
   access possible, spinlock is released
5. sdio irq gets handled, and register access in
   omap_hsmmc_enable_sdio_irq fails with SIGBUS


- Sdio irq is not a dedicated irq line, but is encoded in irqstat. So yoiu need
  to read irqstat to know it's an sdio_irq. This is racy as well
- pm_runtime_suspend clears the IRQSTAT register, so the vulnerable
  window is very small.

The single core situation, I think:
1. Runtime_suspend holds the spinock irqsave so irqstat is not
    fetched early by the irq handler
2. irqstat is cleared by pm_runtime_suspend
3. after pm_runtime hook is complete, a pending the irq is handled
   delaying the gating of the clocks, so irqstat is read safely
4 there is never an sdio_irq after pm_suspend ran

In the multi-core case runtime_suspend / irq handler can run in parallel
- irqstat is read without spinlock, so it probably happen before
  gating the clocks
-- if irqstat happens to be already cleared, nothing happens
-- if sdio_irq shall be handled, a spin_lock is taken, which is held by
   runtime_suspend, clocks are probably gated soon after releasing it.

Your solution will probably work, but relying on reading irqstat before
clocks are gated is not really kosher, I think we should abort
runtime_suspend if irqstat is non-nulll and/or indicates an sdio_irq.

multi-core... N#r%gl

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

* Re: [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-03-24  9:30     ` Dmitry Lifshitz
@ 2014-04-18 16:46       ` Tony Lindgren
  0 siblings, 0 replies; 28+ messages in thread
From: Tony Lindgren @ 2014-04-18 16:46 UTC (permalink / raw)
  To: Dmitry Lifshitz
  Cc: Balaji T K, Andreas Fenkart, Chris Ball, Grant Likely,
	Felipe Balbi, zonque, galak, linux-doc, linux-mmc, linux-omap

Hi,

Sorry for the delay, just noticed this one.

* Dmitry Lifshitz <lifshitz@compulab.co.il> [140324 02:34]:
> Hi,
> 
> I've tested the branch omap_hsmmc_sdio_irq_devm_cleanup on custom OMAP5
> based board.
> We have mwifiex connected to MMC3.
> 
> I used two approaches:
> 
> * Using dat1 line as GPIO with dynamic remuxing
> * Using separate GPIO connected to dat1 with static mux settings
> 
> Both works just fine. Thanks Andreas for a great solution.
> 
> I have just one issue - how to define GPIO IRQ using DT in OMAP5 case.
> As a temporary hack for testing I used a fixed GPIO number and gpio_to_irq()
> call in omap_hsmmc_configure_wake_irq().
> 
> OMAP5 MMC3 has the following DT entry (omap5.dtsi):
> 
>                 mmc3: mmc@480ad000 {
>                         compatible = "ti,omap4-hsmmc";
>                         reg = <0x480ad000 0x400>;
>                         interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
>                         ti,hwmods = "mmc3";
>                         ti,needs-special-reset;
>                         dmas = <&sdma 77>, <&sdma 78>;
>                         dma-names = "tx", "rx";
>                 };
> 
> What should be by board MMC3 setup?

You need to use the interrupts-extended property in omap5.dtsi
for the MMC interrupt. And if using the wake-up interrupts
for your board, you need to sepcify interrupts-extended in your
board file again to add the second wake-up interrupt.

If using the async wake-up path, the wake-up interrupt is the
OMAP5_CORE_IOPAD() or OMAP5_WKUP_IOPAD() pinctrl register offset
for that pin. If remuxing to GPIO for the wake-up, the wake-up
interrupt is the standard gpio interrupt property.

Then you also need this patch to avoid warnings during boot:

http://lkml.org/lkml/2014/4/10/620

After the above gets merged, we can apply the serial wake-up
patch too:

http://lists.infradead.org/pipermail/linux-arm-kernel/2013-November/213873.html

That shows you some examples on how to specify the wake-up
interrupt.

Regards,

Tony

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

* Re: [PATCH 9/9] mmc: omap_hsmmc: enable wakeup event for sdio
  2014-03-21 16:17     ` [PATCH 9/9] mmc: omap_hsmmc: enable wakeup event for sdio Balaji T K
@ 2014-05-02 15:33       ` Balaji T K
  0 siblings, 0 replies; 28+ messages in thread
From: Balaji T K @ 2014-05-02 15:33 UTC (permalink / raw)
  To: Balaji T K; +Cc: linux-mmc, chris, linux-omap, Andreas Fenkart

On Friday 21 March 2014 09:47 PM, Balaji T K wrote:
> To detect sdio irqs properly without spurious events,
> OMAP4 needs IWE in CON and CTPL, CLKEXTFREE in HCTL to be set
>
> Signed-off-by: Balaji T K <balajitk@ti.com>
Hi Andreas,

Can you please test this patch on top of your current(v10) series
and confirm that there are no regression due to this
additional programming on your am335x platform.

Thanks and Regards,
Balaji T K
> ---
>   drivers/mmc/host/omap_hsmmc.c |   13 ++++++++++++-
>   1 files changed, 12 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 2482783..120f7cf 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -94,7 +94,10 @@
>   #define BCE			(1 << 1)
>   #define FOUR_BIT		(1 << 1)
>   #define HSPE			(1 << 2)
> +#define IWE			(1 << 24)
>   #define DDR			(1 << 19)
> +#define CLKEXTFREE		(1 << 16)
> +#define CTPL			(1 << 11)
>   #define DW8			(1 << 5)
>   #define OD			0x1
>   #define STAT_CLEAR		0xFFFFFFFF
> @@ -732,6 +735,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
>   		capa = VS18;
>   	}
>
> +	if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
> +		hctl |= IWE;
>   	OMAP_HSMMC_WRITE(host->base, HCTL,
>   			OMAP_HSMMC_READ(host->base, HCTL) | hctl);
>
> @@ -1728,7 +1733,7 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_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;
> +	u32 irq_mask, con;
>   	unsigned long flags;
>
>   	if (enable)
> @@ -1737,14 +1742,18 @@ static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
>   	if (host->flags & HSMMC_RUNTIME_SUSPENDED)
>   		goto out;
>
> +	con = OMAP_HSMMC_READ(host->base, CON);
>   	irq_mask = OMAP_HSMMC_READ(host->base, ISE);
>   	if (enable) {
>   		host->flags |= HSMMC_SDIO_IRQ_ENABLED;
>   		irq_mask |= CIRQ_EN;
> +		con |= CTPL | CLKEXTFREE;
>   	} else {
>   		host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
>   		irq_mask &= ~CIRQ_EN;
> +		con &= ~(CTPL | CLKEXTFREE);
>   	}
> +	OMAP_HSMMC_WRITE(host->base, CON, con);
>   	OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
>
>   	/*
> @@ -1801,6 +1810,8 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>   		}
>   		host->flags |= HSMMC_SWAKEUP_QUIRK;
>   	}
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +			 OMAP_HSMMC_READ(host->base, HCTL) | IWE);
>
>   	return 0;
>   }
>


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

end of thread, other threads:[~2014-05-02 15:33 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-21 12:20 [PATCH v9 resend 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
2014-03-21 12:20 ` [PATCH v9 resend 1/3] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
2014-03-21 16:10   ` Balaji T K
2014-03-24  9:30     ` Dmitry Lifshitz
2014-04-18 16:46       ` Tony Lindgren
2014-03-21 16:17   ` [PATCH 0/9] mmc: omap_hsmmc: convert to use devm_* and enable sdio irq Balaji T K
2014-03-21 16:17     ` [PATCH 1/9] mmc: omap_hsmmc: use devm_clk_get Balaji T K
2014-03-21 16:17     ` [PATCH 2/9] mmc: omap_hsmmc: use devm_request_irq Balaji T K
2014-03-21 16:17     ` [PATCH 3/9] mmc: omap_hsmmc: use devm_request_threaded_irq Balaji T K
2014-03-21 16:17     ` [PATCH 4/9] mmc: omap_hsmmc: use devm_request_mem_region Balaji T K
2014-03-21 16:18       ` Felipe Balbi
2014-03-21 16:27         ` Balaji T K
2014-03-21 16:30           ` Felipe Balbi
2014-03-21 16:17     ` [PATCH 5/9] mmc: omap_hsmmc: use devm_ioremap Balaji T K
2014-03-21 16:17     ` [PATCH 6/9] mmc: omap_hsmmc: Enable SDIO interrupt Balaji T K
2014-03-24 12:43       ` Ulf Hansson
2014-03-24 14:59         ` Andreas Fenkart
2014-03-24 16:02           ` Ulf Hansson
2014-03-24 16:34             ` Andreas Fenkart
2014-03-25  8:07               ` Ulf Hansson
2014-03-25 15:19                 ` Balaji T K
2014-03-30 22:43                   ` Andreas Fenkart
2014-03-21 16:17     ` [PATCH 7/9] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Balaji T K
2014-03-21 16:17     ` [PATCH 8/9] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Balaji T K
2014-03-21 16:17     ` [PATCH 9/9] mmc: omap_hsmmc: enable wakeup event for sdio Balaji T K
2014-05-02 15:33       ` Balaji T K
2014-03-21 12:20 ` [PATCH v9 resend 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
2014-03-21 12:20 ` [PATCH v9 resend 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Andreas Fenkart

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.