* [PATCH v6 1/3] mmc: omap_hsmmc: Enable SDIO IRQ.
2013-12-16 11:24 [PATCH v6 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
@ 2013-12-16 11:24 ` Andreas Fenkart
2013-12-21 0:53 ` Tony Lindgren
2013-12-16 11:24 ` [PATCH v6 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
2013-12-16 11:24 ` [PATCH v6 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Andreas Fenkart
2 siblings, 1 reply; 6+ messages in thread
From: Andreas Fenkart @ 2013-12-16 11:24 UTC (permalink / raw)
To: Chris Ball
Cc: Tony Lindgren, Grant Likely, Felipe Balbi, Balaji T K, zonque,
galak, linux-doc, linux-mmc, linux-omap, Andreas Fenkart
For now, only support SDIO interrupt if we are booted with
DT. This is because some platforms need special quirks. And
we don't want to add new legacy mux platform init code
callbacks any longer as we are moving to DT based booting
anyways.
Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
---
drivers/mmc/host/omap_hsmmc.c | 109 ++++++++++++++++++++++++++++----
include/linux/platform_data/mmc-omap.h | 1 +
2 files changed, 97 insertions(+), 13 deletions(-)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 8be5c9f..c197028 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -131,6 +131,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)
@@ -215,6 +216,9 @@ struct omap_hsmmc_host {
int reqs_blocked;
int use_reg;
int req_in_progress;
+ int flags;
+#define HSMMC_SDIO_IRQ_ENABLED (1 << 0) /* SDIO irq enabled */
+
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
@@ -495,27 +499,40 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
struct mmc_command *cmd)
{
- unsigned int irq_mask;
+ u32 irq_mask = INT_EN_MASK;
+ unsigned long flags;
if (host->use_dma)
- irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
- else
- irq_mask = INT_EN_MASK;
+ irq_mask &= ~(BRR_EN | BWR_EN);
/* Disable timeout for erases */
if (cmd->opcode == MMC_ERASE)
irq_mask &= ~DTO_EN;
+ spin_lock_irqsave(&host->irq_lock, flags);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+
+ /* latch pending CIRQ, but don't signal */
+ if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
+ irq_mask |= CIRQ_EN;
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
}
static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
{
- OMAP_HSMMC_WRITE(host->base, ISE, 0);
- OMAP_HSMMC_WRITE(host->base, IE, 0);
+ u32 irq_mask = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+ /* no transfer running, need to signal cirq if enabled */
+ if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
+ irq_mask |= CIRQ_EN;
+ OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+ OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
}
/* Calculate divisor for the given clock frequency */
@@ -1078,8 +1095,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);
@@ -1591,6 +1612,37 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
mmc_slot(host).init_card(card);
}
+static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+ u32 irq_mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+
+ irq_mask = OMAP_HSMMC_READ(host->base, ISE);
+ if (enable) {
+ host->flags |= HSMMC_SDIO_IRQ_ENABLED;
+ irq_mask |= CIRQ_EN;
+ } else {
+ host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
+ irq_mask &= ~CIRQ_EN;
+ }
+ OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
+
+ /*
+ * if enable, piggy back detection on current request
+ * but always disable immediately
+ */
+ if (!host->req_in_progress || !enable)
+ OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+
+ /* flush posted write */
+ OMAP_HSMMC_READ(host->base, IE);
+
+ spin_unlock_irqrestore(&host->irq_lock, flags);
+}
+
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;
@@ -1643,7 +1695,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
@@ -1705,7 +1757,18 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
#endif
#ifdef CONFIG_OF
-static u16 omap4_reg_offset = 0x100;
+struct of_data {
+ u16 offset;
+ int flags;
+};
+
+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 +1779,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,
},
{},
};
@@ -1801,8 +1868,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;
}
}
@@ -2020,6 +2088,21 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
dev_warn(&pdev->dev,
"pins are not configured from the driver\n");
+ /*
+ * For now, only support SDIO interrupt if we are booted with
+ * DT. This is because some platforms need special quirks. And
+ * we don't want to add new legacy mux platform init code
+ * callbacks any longer as we are moving to DT based booting
+ * anyways.
+ */
+ if (pdev->dev.of_node) {
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
+ if (pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
+ /* no wakeup from deeper power states, use polling */
+ mmc->caps &= ~MMC_CAP_SDIO_IRQ;
+ }
+ }
+
omap_hsmmc_protect_card(host);
mmc_add_host(mmc);
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] 6+ messages in thread* Re: [PATCH v6 1/3] mmc: omap_hsmmc: Enable SDIO IRQ.
2013-12-16 11:24 ` [PATCH v6 1/3] " Andreas Fenkart
@ 2013-12-21 0:53 ` Tony Lindgren
0 siblings, 0 replies; 6+ messages in thread
From: Tony Lindgren @ 2013-12-21 0:53 UTC (permalink / raw)
To: Andreas Fenkart
Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque, galak,
linux-doc, linux-mmc, linux-omap
* Andreas Fenkart <afenkart@gmail.com> [131216 03:29]:
> For now, only support SDIO interrupt if we are booted with
> DT. This is because some platforms need special quirks. And
> we don't want to add new legacy mux platform init code
> callbacks any longer as we are moving to DT based booting
> anyways.
>
> Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Acked-by: Tony Lindgren <tony@atomide.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v6 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x.
2013-12-16 11:24 [PATCH v6 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
2013-12-16 11:24 ` [PATCH v6 1/3] " Andreas Fenkart
@ 2013-12-16 11:24 ` Andreas Fenkart
2013-12-21 1:01 ` Tony Lindgren
2013-12-16 11:24 ` [PATCH v6 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux Andreas Fenkart
2 siblings, 1 reply; 6+ messages in thread
From: Andreas Fenkart @ 2013-12-16 11:24 UTC (permalink / raw)
To: Chris Ball
Cc: Tony Lindgren, 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 as well as the implementation
of omap_hsmmc_pin_init.
Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
---
.../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 50 ++++++
drivers/mmc/host/omap_hsmmc.c | 184 ++++++++++++++++++--
2 files changed, 222 insertions(+), 12 deletions(-)
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 c197028..bb01d86 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>
@@ -206,6 +208,7 @@ struct omap_hsmmc_host {
u32 sysctl;
u32 capa;
int irq;
+ int gpio_sdio_irq;
int use_dma, dma_ch;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
@@ -218,11 +221,32 @@ struct omap_hsmmc_host {
int req_in_progress;
int flags;
#define HSMMC_SDIO_IRQ_ENABLED (1 << 0) /* SDIO irq enabled */
+#define HSMMC_SWAKEUP_QUIRK (1 << 1)
+#define HSMMC_CIRQ_GPIO_ENABLED (1 << 2)
struct omap_hsmmc_next next_data;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *fixed, *active, *idle;
struct omap_mmc_platform_data *pdata;
};
+static irqreturn_t omap_hsmmc_cirq(int irq, void *dev_id)
+{
+ struct omap_hsmmc_host *host = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+ if (host->flags & HSMMC_CIRQ_GPIO_ENABLED) {
+ disable_irq_nosync(host->gpio_sdio_irq);
+ host->flags &= ~HSMMC_CIRQ_GPIO_ENABLED;
+ }
+ spin_unlock_irqrestore(&host->irq_lock, flags);
+
+ pm_request_resume(host->dev); /* no use counter */
+
+ return IRQ_HANDLED;
+}
+
static int omap_hsmmc_card_detect(struct device *dev, int slot)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
@@ -476,6 +500,67 @@ 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)
+{
+ int ret;
+
+ host->pinctrl = devm_pinctrl_get(host->dev);
+ if (IS_ERR(host->pinctrl)) {
+ dev_dbg(host->dev, "no pinctrl handle\n");
+ ret = 0;
+ goto out;
+ }
+
+ host->fixed = pinctrl_lookup_state(host->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(host->fixed)) {
+ dev_dbg(host->dev,
+ "pins are not configured from the driver\n");
+ host->fixed = NULL;
+ ret = 0;
+ goto out;
+ }
+
+ ret = pinctrl_select_state(host->pinctrl, host->fixed);
+ if (ret < 0)
+ goto err;
+
+ /* For most cases we don't have wake-ups, and exit after this */
+ host->active = pinctrl_lookup_state(host->pinctrl, "active");
+ if (IS_ERR(host->active)) {
+ ret = PTR_ERR(host->active);
+ host->active = NULL;
+ return 0;
+ }
+
+ host->idle = pinctrl_lookup_state(host->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(host->pinctrl, host->idle);
+ if (ret < 0)
+ goto err;
+
+ ret = pinctrl_select_state(host->pinctrl, host->active);
+ if (ret < 0)
+ goto err;
+
+ dev_info(mmc_dev(host->mmc), "pins configured for wake-up events\n");
+
+ return 0;
+
+err:
+ dev_err(mmc_dev(host->mmc), "pins configuration error: %i\n", ret);
+
+out:
+ return ret;
+}
+
/*
* Start clock to the card
*/
@@ -1854,11 +1939,10 @@ 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, irq2;
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) {
@@ -1891,6 +1975,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (res == NULL || irq < 0)
return -ENXIO;
+ irq2 = 0;
+ if (pdev->dev.of_node)
+ irq2 = 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;
@@ -1912,6 +2000,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->use_dma = 1;
host->dma_ch = -1;
host->irq = irq;
+ host->gpio_sdio_irq = irq2;
host->slot_id = 0;
host->mapbase = res->start + pdata->reg_offset;
host->base = ioremap(host->mapbase, SZ_4K);
@@ -2083,10 +2172,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 are booted with
@@ -2098,8 +2186,28 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (pdev->dev.of_node) {
mmc->caps |= MMC_CAP_SDIO_IRQ;
if (pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
- /* no wakeup from deeper power states, use polling */
- mmc->caps &= ~MMC_CAP_SDIO_IRQ;
+ /* use GPIO to wakeup from deeper power states */
+ if (!host->idle || !host->gpio_sdio_irq) {
+ dev_warn(mmc_dev(host->mmc),
+ "Disable SDIO IRQ workaround, GPIO IRQ or pinctrl idle state missing, falling back to polling\n");
+ mmc->caps &= ~MMC_CAP_SDIO_IRQ;
+ } else {
+ host->flags |= HSMMC_SWAKEUP_QUIRK;
+
+ }
+ }
+
+ if (host->flags & HSMMC_SWAKEUP_QUIRK) {
+ /* prevent auto-enabling of IRQ */
+ irq_set_status_flags(host->gpio_sdio_irq, IRQ_NOAUTOEN);
+ ret = request_irq(host->gpio_sdio_irq, omap_hsmmc_cirq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ mmc_hostname(mmc), host);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc),
+ "Unable to grab GPIO SDIO IRQ\n");
+ goto err_irq_sdio;
+ }
}
}
@@ -2127,7 +2235,13 @@ 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->gpio_sdio_irq)
+ free_irq(host->gpio_sdio_irq, host);
+err_irq_sdio:
+ 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:
if (host->use_reg)
omap_hsmmc_reg_put(host);
@@ -2172,13 +2286,15 @@ 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->gpio_sdio_irq))
+ free_irq(host->gpio_sdio_irq, host);
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);
if (host->rx_chan)
dma_release_channel(host->rx_chan);
+ devm_pinctrl_put(host->pinctrl);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
@@ -2234,6 +2350,9 @@ static int omap_hsmmc_suspend(struct device *dev)
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
+ if (host->flags & HSMMC_SWAKEUP_QUIRK)
+ disable_irq(host->gpio_sdio_irq);
+
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
@@ -2259,6 +2378,9 @@ static int omap_hsmmc_resume(struct device *dev)
omap_hsmmc_protect_card(host);
+ if (host->flags & HSMMC_SWAKEUP_QUIRK)
+ enable_irq(host->gpio_sdio_irq);
+
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
@@ -2274,23 +2396,61 @@ 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;
+ 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->flags & HSMMC_SWAKEUP_QUIRK) {
+ OMAP_HSMMC_WRITE(host->base, ISE, 0);
+ OMAP_HSMMC_WRITE(host->base, IE, 0);
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+
+ ret = pinctrl_select_state(host->pinctrl, host->idle);
+ if (ret < 0)
+ dev_warn(mmc_dev(host->mmc), "Unable to select idle pinmux\n");
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+ if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
+ enable_irq(host->gpio_sdio_irq);
+ host->flags |= HSMMC_CIRQ_GPIO_ENABLED;
+ }
+ spin_unlock_irqrestore(&host->irq_lock, flags);
+ }
+
+ return ret;
}
static int omap_hsmmc_runtime_resume(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_restore(host);
dev_dbg(dev, "enabled\n");
- return 0;
+ if (host->flags & HSMMC_SWAKEUP_QUIRK) {
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+ if (host->flags & HSMMC_CIRQ_GPIO_ENABLED) {
+ disable_irq_nosync(host->gpio_sdio_irq);
+ host->flags &= ~HSMMC_CIRQ_GPIO_ENABLED;
+ }
+ spin_unlock_irqrestore(&host->irq_lock, flags);
+
+ 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);
+ }
+ return ret;
}
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH v6 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x.
2013-12-16 11:24 ` [PATCH v6 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
@ 2013-12-21 1:01 ` Tony Lindgren
0 siblings, 0 replies; 6+ messages in thread
From: Tony Lindgren @ 2013-12-21 1:01 UTC (permalink / raw)
To: Andreas Fenkart
Cc: Chris Ball, Grant Likely, Felipe Balbi, Balaji T K, zonque, galak,
linux-doc, linux-mmc, linux-omap
* Andreas Fenkart <afenkart@gmail.com> [131216 03:29]:
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -2098,8 +2186,28 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
> if (pdev->dev.of_node) {
> mmc->caps |= MMC_CAP_SDIO_IRQ;
> if (pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
> - /* no wakeup from deeper power states, use polling */
> - mmc->caps &= ~MMC_CAP_SDIO_IRQ;
> + /* use GPIO to wakeup from deeper power states */
> + if (!host->idle || !host->gpio_sdio_irq) {
> + dev_warn(mmc_dev(host->mmc),
> + "Disable SDIO IRQ workaround, GPIO IRQ or pinctrl idle state missing, falling back to polling\n");
> + mmc->caps &= ~MMC_CAP_SDIO_IRQ;
> + } else {
> + host->flags |= HSMMC_SWAKEUP_QUIRK;
> +
> + }
> + }
> +
> + if (host->flags & HSMMC_SWAKEUP_QUIRK) {
> + /* prevent auto-enabling of IRQ */
> + irq_set_status_flags(host->gpio_sdio_irq, IRQ_NOAUTOEN);
> + ret = request_irq(host->gpio_sdio_irq, omap_hsmmc_cirq,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + mmc_hostname(mmc), host);
> + if (ret) {
> + dev_err(mmc_dev(host->mmc),
> + "Unable to grab GPIO SDIO IRQ\n");
> + goto err_irq_sdio;
> + }
> }
> }
>
FYI This part needs to be changed a bit for the omaps that have a working
wake-up interrupt. I tried on 3730 with the following patch, but so far it
gets stuck in an infinite SDIO interrupt loop. I'll try to take a look at
it more but may not get to it very soon with the holidays coming up. Also
changed the naming to use wakeirq to make it a bit shorter and generic.
Regards,
Tony
8< ---------------------------------------
--- a/arch/arm/boot/dts/omap3-evm-37xx.dts
+++ b/arch/arm/boot/dts/omap3-evm-37xx.dts
@@ -48,7 +48,7 @@
0x128 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
0x12a (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
0x12c (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
- 0x12e (WAKEUP_EN | PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ 0x12e (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
0x130 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
0x132 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
>;
@@ -81,6 +81,7 @@
};
&mmc2 {
+ interrupts-extended = <&intc 86 &omap3_pmx_core 0x12e>;
pinctrl-names = "default";
pinctrl-0 = <&mmc2_pins>;
};
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -395,7 +395,7 @@
mmc2: mmc@480b4000 {
compatible = "ti,omap3-hsmmc";
reg = <0x480b4000 0x200>;
- interrupts = <86>;
+ interrupts-extended = <&intc 86>;
ti,hwmods = "mmc2";
dmas = <&sdma 47>, <&sdma 48>;
dma-names = "tx", "rx";
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 78e7b80..7da1532 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -182,7 +182,7 @@ struct omap_hsmmc_host {
u32 sysctl;
u32 capa;
int irq;
- int gpio_sdio_irq;
+ int wakeirq;
int use_dma, dma_ch;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
@@ -211,7 +211,7 @@ static irqreturn_t omap_hsmmc_cirq(int irq, void *dev_id)
spin_lock_irqsave(&host->irq_lock, flags);
if (host->flags & HSMMC_CIRQ_GPIO_ENABLED) {
- disable_irq_nosync(host->gpio_sdio_irq);
+ disable_irq_nosync(host->wakeirq);
host->flags &= ~HSMMC_CIRQ_GPIO_ENABLED;
}
spin_unlock_irqrestore(&host->irq_lock, flags);
@@ -1159,7 +1159,8 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
omap_hsmmc_do_irq(host, status);
if (status & CIRQ_EN)
- mmc_signal_sdio_irq(host->mmc);
+ if (host->mmc->sdio_irq_thread)
+ mmc_signal_sdio_irq(host->mmc);
/* Flush posted write */
status = OMAP_HSMMC_READ(host->base, STAT);
@@ -1976,7 +1977,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->use_dma = 1;
host->dma_ch = -1;
host->irq = irq;
- host->gpio_sdio_irq = irq2;
+ host->wakeirq = irq2;
host->slot_id = 0;
host->mapbase = res->start + pdata->reg_offset;
host->base = ioremap(host->mapbase, SZ_4K);
@@ -2153,20 +2154,19 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_SDIO_IRQ;
if (pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
/* use GPIO to wakeup from deeper power states */
- if (!host->idle || !host->gpio_sdio_irq) {
+ if (!host->idle || !host->wakeirq) {
dev_warn(mmc_dev(host->mmc),
"Disable SDIO IRQ workaround, GPIO IRQ or pinctrl idle state missing, falling back to polling\n");
mmc->caps &= ~MMC_CAP_SDIO_IRQ;
- } else {
- host->flags |= HSMMC_SWAKEUP_QUIRK;
-
}
}
- if (host->flags & HSMMC_SWAKEUP_QUIRK) {
+ if (host->wakeirq) {
+ host->flags |= HSMMC_SWAKEUP_QUIRK;
+
/* prevent auto-enabling of IRQ */
- irq_set_status_flags(host->gpio_sdio_irq, IRQ_NOAUTOEN);
- ret = request_irq(host->gpio_sdio_irq, omap_hsmmc_cirq,
+ irq_set_status_flags(host->wakeirq, IRQ_NOAUTOEN);
+ ret = request_irq(host->wakeirq, omap_hsmmc_cirq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
mmc_hostname(mmc), host);
if (ret) {
@@ -2201,8 +2201,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
err_slot_name:
mmc_remove_host(mmc);
- if (host->gpio_sdio_irq)
- free_irq(host->gpio_sdio_irq, host);
+ if (host->wakeirq)
+ free_irq(host->wakeirq, host);
err_irq_sdio:
devm_pinctrl_put(host->pinctrl);
err_pinctrl_state:
@@ -2252,8 +2252,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->gpio_sdio_irq))
- free_irq(host->gpio_sdio_irq, host);
+ if ((host->wakeirq))
+ free_irq(host->wakeirq, host);
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
if (host->tx_chan)
@@ -2317,7 +2317,7 @@ static int omap_hsmmc_suspend(struct device *dev)
}
if (host->flags & HSMMC_SWAKEUP_QUIRK)
- disable_irq(host->gpio_sdio_irq);
+ disable_irq(host->wakeirq);
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
@@ -2345,7 +2345,7 @@ static int omap_hsmmc_resume(struct device *dev)
omap_hsmmc_protect_card(host);
if (host->flags & HSMMC_SWAKEUP_QUIRK)
- enable_irq(host->gpio_sdio_irq);
+ enable_irq(host->wakeirq);
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
@@ -2374,13 +2374,16 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
OMAP_HSMMC_WRITE(host->base, IE, 0);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
- ret = pinctrl_select_state(host->pinctrl, host->idle);
- if (ret < 0)
- dev_warn(mmc_dev(host->mmc), "Unable to select idle pinmux\n");
+ 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");
+ }
spin_lock_irqsave(&host->irq_lock, flags);
if (host->flags & HSMMC_SDIO_IRQ_ENABLED) {
- enable_irq(host->gpio_sdio_irq);
+ enable_irq(host->wakeirq);
host->flags |= HSMMC_CIRQ_GPIO_ENABLED;
}
spin_unlock_irqrestore(&host->irq_lock, flags);
@@ -2403,14 +2406,17 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
spin_lock_irqsave(&host->irq_lock, flags);
if (host->flags & HSMMC_CIRQ_GPIO_ENABLED) {
- disable_irq_nosync(host->gpio_sdio_irq);
+ disable_irq_nosync(host->wakeirq);
host->flags &= ~HSMMC_CIRQ_GPIO_ENABLED;
}
spin_unlock_irqrestore(&host->irq_lock, flags);
- ret = pinctrl_select_state(host->pinctrl, host->active);
- if (ret < 0)
- dev_warn(mmc_dev(host->mmc), "Unable to select active pinmux\n");
+ 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);
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v6 3/3] mmc: omap_hsmmc: Extend debugfs for SDIO IRQ, GPIO and pinmux.
2013-12-16 11:24 [PATCH v6 0/3] mmc: omap_hsmmc: Enable SDIO IRQ Andreas Fenkart
2013-12-16 11:24 ` [PATCH v6 1/3] " Andreas Fenkart
2013-12-16 11:24 ` [PATCH v6 2/3] mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x Andreas Fenkart
@ 2013-12-16 11:24 ` Andreas Fenkart
2 siblings, 0 replies; 6+ messages in thread
From: Andreas Fenkart @ 2013-12-16 11:24 UTC (permalink / raw)
To: Chris Ball
Cc: Tony Lindgren, 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>
---
drivers/mmc/host/omap_hsmmc.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index bb01d86..1037b7d 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
@@ -1789,14 +1790,24 @@ 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);
+ bool suspended;
- seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
- mmc->index, host->context_loss);
- pm_runtime_get_sync(host->dev);
+ seq_printf(s, "mmc%d:\n", mmc->index);
+ seq_printf(s, "sdio irq\t%s\n", ((host->flags & HSMMC_SDIO_IRQ_ENABLED)
+ ? "enabled" : "disabled"));
+ suspended = host->dev->power.runtime_status != RPM_ACTIVE;
+ if (host->flags & HSMMC_SWAKEUP_QUIRK)
+ 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] 6+ messages in thread