All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ.
@ 2014-04-28  7:40 Andreas Fenkart
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-28  7:40 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

Hi Balaji, Tony, all

v10
- bug fix on multi-core, untested
- incorporated changes from Balaji
- use devres / RAII mechanism to configure wake_up /
  sdio irq capabilities
- drop pinctrl state 'active'
  rely on driver-model states 'default', 'idle'
- add specific 'gpio_dat1' state for am335x SWAKEUP hack
- reorganized patches; +1 patch multi-core bugfix / +1 for pinctrl
- rebased 455c6fdbd21916 / cherry-picks from mmc-next

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 (5):
  mmc: omap_hsmmc: Enable SDIO interrupt
  mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq
    detected
  mmc: omap_hsmmc: Extend debugfs by SDIO IRQ handling, runtime state
  mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks
  mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on
    AM335x

 .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |   51 ++++
 drivers/mmc/host/omap_hsmmc.c                      |  285 ++++++++++++++++++--
 include/linux/platform_data/mmc-omap.h             |    1 +
 3 files changed, 316 insertions(+), 21 deletions(-)

-- 
1.7.10.4


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

* [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-28  7:40 [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
@ 2014-04-28  7:40 ` Andreas Fenkart
  2014-04-29  4:42   ` Joel Fernandes
                     ` (4 more replies)
  2014-04-28  7:40 ` [PATCH v10 2/5] mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq detected Andreas Fenkart
                   ` (3 subsequent siblings)
  4 siblings, 5 replies; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-28  7:40 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>

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 272e0ee..700fb91 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,9 @@ struct omap_hsmmc_host {
 	int			req_in_progress;
 	unsigned long		clk_rate;
 	unsigned int		flags;
+#define AUTO_CMD23		(1 << 0)        /* Auto CMD23 support */
+#define HSMMC_SDIO_IRQ_ENABLED	(1 << 1)        /* SDIO irq enabled */
+#define HSMMC_WAKE_IRQ_ENABLED	(1 << 2)
 	struct omap_hsmmc_next	next_data;
 	struct	omap_mmc_platform_data	*pdata;
 };
@@ -510,27 +516,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 */
@@ -681,7 +700,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		&& time_before(jiffies, timeout))
 		;
 
-	omap_hsmmc_disable_irq(host);
+	OMAP_HSMMC_WRITE(host->base, ISE, 0);
+	OMAP_HSMMC_WRITE(host->base, IE, 0);
+	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 
 	/* Do not initialize card-specific things if the power is off */
 	if (host->power_mode == MMC_POWER_OFF)
@@ -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,23 @@ 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 +1680,83 @@ 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_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;
+
+	if (!devres_open_group(host->dev, NULL, GFP_KERNEL))
+		return -ENOMEM;
+
+	/* Prevent auto-enabling of IRQ */
+	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(host->dev, 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");
+		goto err;
+	}
+
+	/*
+	 * 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) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	devres_remove_group(host->dev, NULL);
+	return 0;
+
+err:
+	dev_info(host->dev, "no SDIO IRQ support, falling back to polling\n");
+	devres_release_group(host->dev, NULL);
+	host->wake_irq = 0;
+	return ret;
+}
+
 static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
 {
 	u32 hctl, capa, value;
@@ -1690,7 +1809,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
@@ -1760,6 +1879,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
 static const struct omap_mmc_of_data omap4_mmc_of_data = {
 	.reg_offset = 0x100,
 };
+static const 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[] = {
 	{
@@ -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)
@@ -1911,6 +2039,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;
@@ -2075,6 +2206,20 @@ 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;
+	/* no sdio irq without wake_irq */
+	WARN_ON(!(mmc->caps & MMC_CAP_SDIO_IRQ) != !host->wake_irq);
+
 	omap_hsmmc_protect_card(host);
 
 	mmc_add_host(mmc);
@@ -2201,11 +2346,16 @@ static int omap_hsmmc_suspend(struct device *dev)
 	pm_runtime_get_sync(host->dev);
 
 	if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
-		omap_hsmmc_disable_irq(host);
+		OMAP_HSMMC_WRITE(host->base, ISE, 0);
+		OMAP_HSMMC_WRITE(host->base, IE, 0);
+		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 		OMAP_HSMMC_WRITE(host->base, HCTL,
 				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);
 
@@ -2231,6 +2381,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;
@@ -2246,22 +2399,51 @@ static int omap_hsmmc_resume(struct device *dev)
 static int omap_hsmmc_runtime_suspend(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
+	unsigned long flags;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_save(host);
 	dev_dbg(dev, "disabled\n");
 
+	spin_lock_irqsave(&host->irq_lock, flags);
+	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
+	    (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
+		/* disable sdio irq handling to prevent race */
+		OMAP_HSMMC_WRITE(host->base, ISE, 0);
+		OMAP_HSMMC_WRITE(host->base, IE, 0);
+		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+
+		WARN_ON(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 0;
 }
 
 static int omap_hsmmc_runtime_resume(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
+	unsigned long flags;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_restore(host);
 	dev_dbg(dev, "enabled\n");
 
+	spin_lock_irqsave(&host->irq_lock, flags);
+	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
+	    (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
+		/* sdio irq flag can't change while in runtime suspend */
+		if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
+			disable_irq(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 0;
 }
 
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] 15+ messages in thread

* [PATCH v10 2/5] mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq detected
  2014-04-28  7:40 [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
@ 2014-04-28  7:40 ` Andreas Fenkart
  2014-05-02 14:55   ` Balaji T K
  2014-04-28  7:40 ` [PATCH v10 3/5] mmc: omap_hsmmc: Extend debugfs by SDIO IRQ handling, runtime state Andreas Fenkart
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-28  7:40 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

on multicores, an sdio irq handler could be running in parallel to
runtime suspend. In the worst case it could be waiting for the spinlock
held by the runtime suspend. When runtime suspend is complete and the
functional clock (fclk) turned off, the irq handler will continue and
cause a SIGBUS on the first register access.

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 700fb91..e675042 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
@@ -2400,6 +2401,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 {
 	struct omap_hsmmc_host *host;
 	unsigned long flags;
+	int ret = 0;
 
 	host = platform_get_drvdata(to_platform_device(dev));
 	omap_hsmmc_context_save(host);
@@ -2411,14 +2413,29 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 		/* disable sdio irq handling to prevent race */
 		OMAP_HSMMC_WRITE(host->base, ISE, 0);
 		OMAP_HSMMC_WRITE(host->base, IE, 0);
-		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+
+		if (!(OMAP_HSMMC_READ(host->base, PSTATE) & BIT(21))) {
+			/*
+			 * dat1 line low, pending sdio irq
+			 * race condition: possible irq handler running on
+			 * multi-core, abort
+			 */
+			dev_dbg(dev, "pending sdio irq, abort suspend\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);
+			pm_runtime_mark_last_busy(dev);
+			ret = -EBUSY;
+			goto abort;
+		}
 
 		WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
 		enable_irq(host->wake_irq);
 		host->flags |= HSMMC_WAKE_IRQ_ENABLED;
 	}
+abort:
 	spin_unlock_irqrestore(&host->irq_lock, flags);
-	return 0;
+	return ret;
 }
 
 static int omap_hsmmc_runtime_resume(struct device *dev)
-- 
1.7.10.4


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

* [PATCH v10 3/5] mmc: omap_hsmmc: Extend debugfs by SDIO IRQ handling, runtime state
  2014-04-28  7:40 [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
  2014-04-28  7:40 ` [PATCH v10 2/5] mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq detected Andreas Fenkart
@ 2014-04-28  7:40 ` Andreas Fenkart
  2014-04-28  7:40 ` [PATCH v10 4/5] mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks Andreas Fenkart
  2014-04-28  7:41 ` [PATCH v10 5/5] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
  4 siblings, 0 replies; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-28  7:40 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 e675042..76fe3bd 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1819,14 +1819,28 @@ 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);
+	suspended = host->dev->power.runtime_status != RPM_ACTIVE;
+	seq_printf(s, "runtime state\t%s\n", (suspended ?  "idle" : "active"));
 
+	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] 15+ messages in thread

* [PATCH v10 4/5] mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks
  2014-04-28  7:40 [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
                   ` (2 preceding siblings ...)
  2014-04-28  7:40 ` [PATCH v10 3/5] mmc: omap_hsmmc: Extend debugfs by SDIO IRQ handling, runtime state Andreas Fenkart
@ 2014-04-28  7:40 ` Andreas Fenkart
  2014-05-02 14:38   ` Balaji T K
  2014-04-28  7:41 ` [PATCH v10 5/5] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
  4 siblings, 1 reply; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-28  7:40 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

These are predefined states of the driver model. When not present,
as if not set in the device tree, they simple become no-ops.
So it is always safe to call them.
This is not the simplest implementation, on AM335x at least, we could
witch to idle at any point in the suspend hook, only the default state
needs to be set before writing to the irq registers or an IRQ might get
lost.

Signed-off-by: Andreas Fenkart <afenkart@gmail.com>

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 76fe3bd..9cc0d21 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1992,7 +1992,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);
@@ -2215,11 +2214,7 @@ 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");
+	pinctrl_pm_select_default_state(host->dev);
 
 	/*
 	 * For now, only support SDIO interrupt if we have a separate
@@ -2443,9 +2438,13 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 			goto abort;
 		}
 
+		pinctrl_pm_select_idle_state(dev);
+
 		WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
 		enable_irq(host->wake_irq);
 		host->flags |= HSMMC_WAKE_IRQ_ENABLED;
+	} else {
+		pinctrl_pm_select_idle_state(dev);
 	}
 abort:
 	spin_unlock_irqrestore(&host->irq_lock, flags);
@@ -2470,9 +2469,14 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
 			host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
 		}
 
+		pinctrl_pm_select_default_state(host->dev);
+
+		/* irq lost, if pinmux incorrect */
 		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 		OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
 		OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
+	} else {
+		pinctrl_pm_select_default_state(host->dev);
 	}
 	spin_unlock_irqrestore(&host->irq_lock, flags);
 	return 0;
-- 
1.7.10.4


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

* [PATCH v10 5/5] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x
  2014-04-28  7:40 [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
                   ` (3 preceding siblings ...)
  2014-04-28  7:40 ` [PATCH v10 4/5] mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks Andreas Fenkart
@ 2014-04-28  7:41 ` Andreas Fenkart
  2014-05-02 14:30   ` Balaji T K
  4 siblings, 1 reply; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-28  7:41 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 by Tony Lindgren. Code contributions from
Tony Lindgren and 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/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index ce80561..4767cd1 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -56,3 +56,54 @@ 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", "idle" and "gpio_dat1", 2) the gpio detecting
+sdio irq in suspend and 3) the compatibe section, see example below.
+The MMC driver will then toggle between gpio_dat1 and default 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", "idle", "sleep", "gpio_dat1"
+		pinctrl-0 = <&mmc1_pins>;
+		pinctrl-1 = <&mmc1_idle>;
+		pinctrl-2 = <&mmc1_sleep>;
+		pinctrl-3 = <&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 9cc0d21..8661e1f 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -213,7 +213,10 @@ struct omap_hsmmc_host {
 #define AUTO_CMD23		(1 << 0)        /* Auto CMD23 support */
 #define HSMMC_SDIO_IRQ_ENABLED	(1 << 1)        /* SDIO irq enabled */
 #define HSMMC_WAKE_IRQ_ENABLED	(1 << 2)
+#define HSMMC_SWAKEUP_QUIRK	(1 << 3)
 	struct omap_hsmmc_next	next_data;
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*gpio_pinmux;
 	struct	omap_mmc_platform_data	*pdata;
 };
 
@@ -1744,8 +1747,28 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
 	 */
 	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
-		ret = -ENODEV;
-		goto err;
+		if (IS_ERR(host->dev->pins->default_state)) {
+			dev_info(host->dev, "missing default pinctrl state\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		host->pinctrl = devm_pinctrl_get(host->dev);
+		if (IS_ERR(host->pinctrl)) {
+			dev_warn(host->dev, "no pinctrl handle\n");
+			ret = -ENODEV;
+			goto err;
+		}
+
+		host->gpio_pinmux = pinctrl_lookup_state(host->pinctrl,
+							 "gpio_dat1");
+		if (IS_ERR(host->gpio_pinmux)) {
+			dev_info(host->dev, "missing \"gpio_dat1\" pinctrl state\n");
+			ret = -ENODEV;
+			goto err;
+		}
+
+		host->flags |= HSMMC_SWAKEUP_QUIRK;
 	}
 
 	devres_remove_group(host->dev, NULL);
@@ -2438,7 +2461,10 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 			goto abort;
 		}
 
-		pinctrl_pm_select_idle_state(dev);
+		if (host->flags & HSMMC_SWAKEUP_QUIRK)
+			pinctrl_select_state(host->pinctrl, host->gpio_pinmux);
+		else
+			pinctrl_pm_select_idle_state(dev);
 
 		WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
 		enable_irq(host->wake_irq);
-- 
1.7.10.4


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

* Re: [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
@ 2014-04-29  4:42   ` Joel Fernandes
  2014-04-29  4:43   ` Joel Fernandes
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Joel Fernandes @ 2014-04-29  4:42 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-doc, linux-omap

Minor nit...

On 04/28/2014 02:40 AM, Andreas Fenkart wrote:
[..]
>  	/* Do not initialize card-specific things if the power is off */
>  	if (host->power_mode == MMC_POWER_OFF)
> @@ -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,23 @@ 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);

Do you need to save restore flags here? IRQ is already disabled since
you're in hard IRQ context so spin_lock() seems sufficient.

> +	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);

spin_unlock() seems sufficient.

regards,
-Joel



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

* Re: [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
  2014-04-29  4:42   ` Joel Fernandes
@ 2014-04-29  4:43   ` Joel Fernandes
  2014-04-29  9:32   ` Andreas Müller
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Joel Fernandes @ 2014-04-29  4:43 UTC (permalink / raw)
  To: Andreas Fenkart, Tony Lindgren
  Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque,
	galak, linux-doc, linux-mmc, linux-omap

Minor nit...

On 04/28/2014 02:40 AM, Andreas Fenkart wrote:
[..]
>  	/* Do not initialize card-specific things if the power is off */
>  	if (host->power_mode == MMC_POWER_OFF)
> @@ -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,23 @@ 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);

Do you need to save restore flags here? IRQ is already disabled since
you're in hard IRQ context so spin_lock() seems sufficient.

> +	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);

spin_unlock() seems sufficient.

regards,
-Joel


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

* Re: [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
  2014-04-29  4:42   ` Joel Fernandes
  2014-04-29  4:43   ` Joel Fernandes
@ 2014-04-29  9:32   ` Andreas Müller
  2014-04-30 12:23   ` Andreas Müller
  2014-05-02 15:20   ` Balaji T K
  4 siblings, 0 replies; 15+ messages in thread
From: Andreas Müller @ 2014-04-29  9:32 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi,
	Balaji T K, zonque, galak, linux-doc, linux-mmc, linux-omap

On Mon, Apr 28, 2014 at 9:40 AM, Andreas Fenkart <afenkart@gmail.com> wrote:
> 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>
>
> 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 272e0ee..700fb91 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,9 @@ struct omap_hsmmc_host {
>         int                     req_in_progress;
>         unsigned long           clk_rate;
>         unsigned int            flags;
> +#define AUTO_CMD23             (1 << 0)        /* Auto CMD23 support */
> +#define HSMMC_SDIO_IRQ_ENABLED (1 << 1)        /* SDIO irq enabled */
> +#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
>         struct omap_hsmmc_next  next_data;
>         struct  omap_mmc_platform_data  *pdata;
>  };
> @@ -510,27 +516,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 */
> @@ -681,7 +700,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
>                 && time_before(jiffies, timeout))
>                 ;
>
> -       omap_hsmmc_disable_irq(host);
> +       OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +       OMAP_HSMMC_WRITE(host->base, IE, 0);
> +       OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>
>         /* Do not initialize card-specific things if the power is off */
>         if (host->power_mode == MMC_POWER_OFF)
> @@ -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,23 @@ 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 +1680,83 @@ 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_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;
> +
> +       if (!devres_open_group(host->dev, NULL, GFP_KERNEL))
> +               return -ENOMEM;
> +
> +       /* Prevent auto-enabling of IRQ */
> +       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
> +       ret = devm_request_irq(host->dev, 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");
> +               goto err;
> +       }
> +
> +       /*
> +        * 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) {
> +               ret = -ENODEV;
> +               goto err;
> +       }
> +
> +       devres_remove_group(host->dev, NULL);
> +       return 0;
> +
> +err:
> +       dev_info(host->dev, "no SDIO IRQ support, falling back to polling\n");
> +       devres_release_group(host->dev, NULL);
> +       host->wake_irq = 0;
> +       return ret;
> +}
> +
>  static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>  {
>         u32 hctl, capa, value;
> @@ -1690,7 +1809,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
> @@ -1760,6 +1879,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
>  static const struct omap_mmc_of_data omap4_mmc_of_data = {
>         .reg_offset = 0x100,
>  };
> +static const 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[] = {
>         {
> @@ -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)
> @@ -1911,6 +2039,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;
> @@ -2075,6 +2206,20 @@ 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;
> +       /* no sdio irq without wake_irq */
> +       WARN_ON(!(mmc->caps & MMC_CAP_SDIO_IRQ) != !host->wake_irq);
> +
>         omap_hsmmc_protect_card(host);
>
>         mmc_add_host(mmc);
> @@ -2201,11 +2346,16 @@ static int omap_hsmmc_suspend(struct device *dev)
>         pm_runtime_get_sync(host->dev);
>
>         if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
> -               omap_hsmmc_disable_irq(host);
> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>                 OMAP_HSMMC_WRITE(host->base, HCTL,
>                                 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);
>
> @@ -2231,6 +2381,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))
^ Shouldn't this be &&?
> +               enable_irq(host->wake_irq);
> +
>         pm_runtime_mark_last_busy(host->dev);
>         pm_runtime_put_autosuspend(host->dev);
>         return 0;
> @@ -2246,22 +2399,51 @@ static int omap_hsmmc_resume(struct device *dev)
>  static int omap_hsmmc_runtime_suspend(struct device *dev)
>  {
>         struct omap_hsmmc_host *host;
> +       unsigned long flags;
>
>         host = platform_get_drvdata(to_platform_device(dev));
>         omap_hsmmc_context_save(host);
>         dev_dbg(dev, "disabled\n");
>
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> +           (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
> +               /* disable sdio irq handling to prevent race */
> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +
> +               WARN_ON(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 0;
>  }
>
>  static int omap_hsmmc_runtime_resume(struct device *dev)
>  {
>         struct omap_hsmmc_host *host;
> +       unsigned long flags;
>
>         host = platform_get_drvdata(to_platform_device(dev));
>         omap_hsmmc_context_restore(host);
>         dev_dbg(dev, "enabled\n");
>
> +       spin_lock_irqsave(&host->irq_lock, flags);
> +       if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> +           (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
> +               /* sdio irq flag can't change while in runtime suspend */
> +               if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
> +                       disable_irq(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 0;
>  }
>
> 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
                     ` (2 preceding siblings ...)
  2014-04-29  9:32   ` Andreas Müller
@ 2014-04-30 12:23   ` Andreas Müller
  2014-04-30 21:04     ` Andreas Fenkart
  2014-05-02 15:20   ` Balaji T K
  4 siblings, 1 reply; 15+ messages in thread
From: Andreas Müller @ 2014-04-30 12:23 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi,
	Balaji T K, zonque, galak, linux-doc, linux-mmc, linux-omap

On Mon, Apr 28, 2014 at 9:40 AM, Andreas Fenkart <afenkart@gmail.com> wrote:
> @@ -2201,11 +2346,16 @@ static int omap_hsmmc_suspend(struct device *dev)
>         pm_runtime_get_sync(host->dev);
>
>         if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
> -               omap_hsmmc_disable_irq(host);
> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>                 OMAP_HSMMC_WRITE(host->base, HCTL,
>                                 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);
>
> @@ -2231,6 +2381,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;
Maybe I misunderstand something here but shouldn't
disable_irq/enable_irq be swapped here?

regards

Andreas

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

* Re: [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-30 12:23   ` Andreas Müller
@ 2014-04-30 21:04     ` Andreas Fenkart
  0 siblings, 0 replies; 15+ messages in thread
From: Andreas Fenkart @ 2014-04-30 21:04 UTC (permalink / raw)
  To: Andreas Müller
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi,
	Balaji T K, Daniel Mack, Kumar Gala, linux-doc, linux-mmc,
	linux-omap

Hi Andreas,

2014-04-30 14:23 GMT+02:00 Andreas Müller <schnitzeltony@googlemail.com>:
> On Mon, Apr 28, 2014 at 9:40 AM, Andreas Fenkart <afenkart@gmail.com> wrote:
>> @@ -2201,11 +2346,16 @@ static int omap_hsmmc_suspend(struct device *dev)
>>         pm_runtime_get_sync(host->dev);
>>
>>         if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
>> -               omap_hsmmc_disable_irq(host);
>> +               OMAP_HSMMC_WRITE(host->base, ISE, 0);
>> +               OMAP_HSMMC_WRITE(host->base, IE, 0);
>> +               OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>>                 OMAP_HSMMC_WRITE(host->base, HCTL,
>>                                 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);
>> +

I think it says, "Do you want to wake up from deep power states when
an SDIO IRQ is pending"
Will try to bring this more to the point

>>         if (host->dbclk)
>>                 clk_disable_unprepare(host->dbclk);
>>
>> @@ -2231,6 +2381,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))

you're right should be '&&'

>> +               enable_irq(host->wake_irq);
>> +
>>         pm_runtime_mark_last_busy(host->dev);
>>         pm_runtime_put_autosuspend(host->dev);
>>         return 0;
> Maybe I misunderstand something here but shouldn't
> disable_irq/enable_irq be swapped here?

/Andreas

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

* Re: [PATCH v10 5/5] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x
  2014-04-28  7:41 ` [PATCH v10 5/5] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
@ 2014-05-02 14:30   ` Balaji T K
  0 siblings, 0 replies; 15+ messages in thread
From: Balaji T K @ 2014-05-02 14:30 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi, zonque,
	galak, linux-doc, linux-mmc, linux-omap

On Monday 28 April 2014 01:11 PM, Andreas Fenkart wrote:
> 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 by Tony Lindgren. Code contributions from
> Tony Lindgren and Balaji T K <balajitk@ti.com>
>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>

Thanks Andreas for the patch series.
Few comments ...

>
> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> index ce80561..4767cd1 100644
> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> @@ -56,3 +56,54 @@ 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", "idle" and "gpio_dat1", 2) the gpio detecting
> +sdio irq in suspend and 3) the compatibe section, see example below.
> +The MMC driver will then toggle between gpio_dat1 and default 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", "idle", "sleep", "gpio_dat1"
> +		pinctrl-0 = <&mmc1_pins>;
> +		pinctrl-1 = <&mmc1_idle>;
> +		pinctrl-2 = <&mmc1_sleep>;
> +		pinctrl-3 = <&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 9cc0d21..8661e1f 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -213,7 +213,10 @@ struct omap_hsmmc_host {
>   #define AUTO_CMD23		(1 << 0)        /* Auto CMD23 support */
>   #define HSMMC_SDIO_IRQ_ENABLED	(1 << 1)        /* SDIO irq enabled */
>   #define HSMMC_WAKE_IRQ_ENABLED	(1 << 2)
> +#define HSMMC_SWAKEUP_QUIRK	(1 << 3)
>   	struct omap_hsmmc_next	next_data;
> +	struct pinctrl		*pinctrl;
> +	struct pinctrl_state	*gpio_pinmux;
>   	struct	omap_mmc_platform_data	*pdata;
>   };
>
> @@ -1744,8 +1747,28 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>   	 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
>   	 */
>   	if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
> -		ret = -ENODEV;
> -		goto err;
> +		if (IS_ERR(host->dev->pins->default_state)) {
> +			dev_info(host->dev, "missing default pinctrl state\n");
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +
> +		host->pinctrl = devm_pinctrl_get(host->dev);
> +		if (IS_ERR(host->pinctrl)) {
> +			dev_warn(host->dev, "no pinctrl handle\n");
> +			ret = -ENODEV;
> +			goto err;
> +		}
> +
> +		host->gpio_pinmux = pinctrl_lookup_state(host->pinctrl,
> +							 "gpio_dat1");
> +		if (IS_ERR(host->gpio_pinmux)) {
> +			dev_info(host->dev, "missing \"gpio_dat1\" pinctrl state\n");
> +			ret = -ENODEV;
> +			goto err;
> +		}
> +
> +		host->flags |= HSMMC_SWAKEUP_QUIRK;
>   	}
>
>   	devres_remove_group(host->dev, NULL);
> @@ -2438,7 +2461,10 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
>   			goto abort;
>   		}
>
> -		pinctrl_pm_select_idle_state(dev);
> +		if (host->flags & HSMMC_SWAKEUP_QUIRK)
> +			pinctrl_select_state(host->pinctrl, host->gpio_pinmux);

Why not combine "gpio_dat1" state and "idle", update documentation to have
gpio remuxed in idle state for SoCs with swakeup quirk,
Since I don't see any use case where both idle/gpio_pinmux are getting used.

> +		else
> +			pinctrl_pm_select_idle_state(dev);
>
>   		WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
>   		enable_irq(host->wake_irq);
>


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

* Re: [PATCH v10 4/5] mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks
  2014-04-28  7:40 ` [PATCH v10 4/5] mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks Andreas Fenkart
@ 2014-05-02 14:38   ` Balaji T K
  0 siblings, 0 replies; 15+ messages in thread
From: Balaji T K @ 2014-05-02 14:38 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi, zonque,
	galak, linux-doc, linux-mmc, linux-omap

On Monday 28 April 2014 01:10 PM, Andreas Fenkart wrote:
> These are predefined states of the driver model. When not present,
> as if not set in the device tree, they simple become no-ops.
> So it is always safe to call them.
> This is not the simplest implementation, on AM335x at least, we could
> witch to idle at any point in the suspend hook, only the default state
> needs to be set before writing to the irq registers or an IRQ might get
> lost.
>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 76fe3bd..9cc0d21 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -1992,7 +1992,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);
> @@ -2215,11 +2214,7 @@ 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");
> +	pinctrl_pm_select_default_state(host->dev);

This can be removed, since device core layer sets pin mux to "default" state
before probe.



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

* Re: [PATCH v10 2/5] mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq detected
  2014-04-28  7:40 ` [PATCH v10 2/5] mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq detected Andreas Fenkart
@ 2014-05-02 14:55   ` Balaji T K
  0 siblings, 0 replies; 15+ messages in thread
From: Balaji T K @ 2014-05-02 14:55 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi, zonque,
	galak, linux-doc, linux-mmc, linux-omap

On Monday 28 April 2014 01:10 PM, Andreas Fenkart wrote:
> on multicores, an sdio irq handler could be running in parallel to
> runtime suspend. In the worst case it could be waiting for the spinlock
> held by the runtime suspend. When runtime suspend is complete and the
> functional clock (fclk) turned off, the irq handler will continue and
> cause a SIGBUS on the first register access.
>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 700fb91..e675042 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
> @@ -2400,6 +2401,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
>   {
>   	struct omap_hsmmc_host *host;
>   	unsigned long flags;
> +	int ret = 0;
>
>   	host = platform_get_drvdata(to_platform_device(dev));
>   	omap_hsmmc_context_save(host);
> @@ -2411,14 +2413,29 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
>   		/* disable sdio irq handling to prevent race */
>   		OMAP_HSMMC_WRITE(host->base, ISE, 0);
>   		OMAP_HSMMC_WRITE(host->base, IE, 0);
> -		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +
> +		if (!(OMAP_HSMMC_READ(host->base, PSTATE) & BIT(21))) {

Please use #define for BIT(21), something like DLEV_DAT1


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

* Re: [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt
  2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
                     ` (3 preceding siblings ...)
  2014-04-30 12:23   ` Andreas Müller
@ 2014-05-02 15:20   ` Balaji T K
  4 siblings, 0 replies; 15+ messages in thread
From: Balaji T K @ 2014-05-02 15:20 UTC (permalink / raw)
  To: Andreas Fenkart
  Cc: Tony Lindgren, Chris Ball, Grant Likely, Felipe Balbi, zonque,
	galak, linux-doc, linux-mmc, linux-omap

On Monday 28 April 2014 01:10 PM, Andreas Fenkart wrote:
> 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>
>
> 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 272e0ee..700fb91 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,9 @@ struct omap_hsmmc_host {
>   	int			req_in_progress;
>   	unsigned long		clk_rate;
>   	unsigned int		flags;
> +#define AUTO_CMD23		(1 << 0)        /* Auto CMD23 support */
> +#define HSMMC_SDIO_IRQ_ENABLED	(1 << 1)        /* SDIO irq enabled */
> +#define HSMMC_WAKE_IRQ_ENABLED	(1 << 2)
>   	struct omap_hsmmc_next	next_data;
>   	struct	omap_mmc_platform_data	*pdata;
>   };
> @@ -510,27 +516,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 */
> @@ -681,7 +700,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
>   		&& time_before(jiffies, timeout))
>   		;
>
> -	omap_hsmmc_disable_irq(host);
> +	OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +	OMAP_HSMMC_WRITE(host->base, IE, 0);
> +	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>
>   	/* Do not initialize card-specific things if the power is off */
>   	if (host->power_mode == MMC_POWER_OFF)
> @@ -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,23 @@ 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 +1680,83 @@ 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_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;
> +
> +	if (!devres_open_group(host->dev, NULL, GFP_KERNEL))
> +		return -ENOMEM;

How about using devm_free_irq on error path instead ?

> +
> +	/* Prevent auto-enabling of IRQ */
> +	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
> +	ret = devm_request_irq(host->dev, 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");
> +		goto err;
> +	}
> +
> +	/*
> +	 * 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) {
> +		ret = -ENODEV;
> +		goto err;
> +	}
> +
> +	devres_remove_group(host->dev, NULL);
> +	return 0;
> +
> +err:
> +	dev_info(host->dev, "no SDIO IRQ support, falling back to polling\n");
> +	devres_release_group(host->dev, NULL);
> +	host->wake_irq = 0;
> +	return ret;
> +}
> +
>   static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
>   {
>   	u32 hctl, capa, value;
> @@ -1690,7 +1809,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
> @@ -1760,6 +1879,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
>   static const struct omap_mmc_of_data omap4_mmc_of_data = {
>   	.reg_offset = 0x100,
>   };
> +static const 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[] = {
>   	{
> @@ -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

Looks like spurious/unrelated change

>   #endif
>
>   static int omap_hsmmc_probe(struct platform_device *pdev)
> @@ -1911,6 +2039,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;
> @@ -2075,6 +2206,20 @@ 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;
> +	/* no sdio irq without wake_irq */
> +	WARN_ON(!(mmc->caps & MMC_CAP_SDIO_IRQ) != !host->wake_irq);

IMHO, WARN_ON is not needed here.
Since omap_hsmmc_configure_wake_irq already checks for host->wake_irq,
may be increase the debug level to dev_warn in error handling path.

> +
>   	omap_hsmmc_protect_card(host);
>
>   	mmc_add_host(mmc);
> @@ -2201,11 +2346,16 @@ static int omap_hsmmc_suspend(struct device *dev)
>   	pm_runtime_get_sync(host->dev);
>
>   	if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
> -		omap_hsmmc_disable_irq(host);
> +		OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +		OMAP_HSMMC_WRITE(host->base, IE, 0);
> +		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
>   		OMAP_HSMMC_WRITE(host->base, HCTL,
>   				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);
>
> @@ -2231,6 +2381,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;
> @@ -2246,22 +2399,51 @@ static int omap_hsmmc_resume(struct device *dev)
>   static int omap_hsmmc_runtime_suspend(struct device *dev)
>   {
>   	struct omap_hsmmc_host *host;
> +	unsigned long flags;
>
>   	host = platform_get_drvdata(to_platform_device(dev));
>   	omap_hsmmc_context_save(host);
>   	dev_dbg(dev, "disabled\n");
>
> +	spin_lock_irqsave(&host->irq_lock, flags);
> +	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> +	    (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
> +		/* disable sdio irq handling to prevent race */
> +		OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +		OMAP_HSMMC_WRITE(host->base, IE, 0);
> +		OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +
> +		WARN_ON(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 0;
>   }
>
>   static int omap_hsmmc_runtime_resume(struct device *dev)
>   {
>   	struct omap_hsmmc_host *host;
> +	unsigned long flags;
>
>   	host = platform_get_drvdata(to_platform_device(dev));
>   	omap_hsmmc_context_restore(host);
>   	dev_dbg(dev, "enabled\n");
>
> +	spin_lock_irqsave(&host->irq_lock, flags);
> +	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> +	    (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
> +		/* sdio irq flag can't change while in runtime suspend */
> +		if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
> +			disable_irq(host->wake_irq);

shouldn't this be disable_irq_nosync


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

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

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-28  7:40 [PATCH v10 0/5] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
2014-04-28  7:40 ` [PATCH v10 1/5] mmc: omap_hsmmc: Enable SDIO interrupt Andreas Fenkart
2014-04-29  4:42   ` Joel Fernandes
2014-04-29  4:43   ` Joel Fernandes
2014-04-29  9:32   ` Andreas Müller
2014-04-30 12:23   ` Andreas Müller
2014-04-30 21:04     ` Andreas Fenkart
2014-05-02 15:20   ` Balaji T K
2014-04-28  7:40 ` [PATCH v10 2/5] mmc: omap_hsmmc: bug: abort runtime suspend if pending sdio irq detected Andreas Fenkart
2014-05-02 14:55   ` Balaji T K
2014-04-28  7:40 ` [PATCH v10 3/5] mmc: omap_hsmmc: Extend debugfs by SDIO IRQ handling, runtime state Andreas Fenkart
2014-04-28  7:40 ` [PATCH v10 4/5] mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks Andreas Fenkart
2014-05-02 14:38   ` Balaji T K
2014-04-28  7:41 ` [PATCH v10 5/5] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
2014-05-02 14:30   ` Balaji T K

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.