Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: omap_hsmmc: Enable SDIO interrupt

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

Acked-by: Balaji T K <balajitk@ti.com>
Signed-off-by: Andreas Fenkart <afenkart@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Andreas Fenkart and committed by
Ulf Hansson
2cd3a2a5 be19c405

+191 -12
+1
Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
··· 12 12 Should be "ti,omap3-hsmmc", for OMAP3 controllers 13 13 Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0 14 14 Should be "ti,omap4-hsmmc", for OMAP4 controllers 15 + Should be "ti,am33xx-hsmmc", for AM335x controllers 15 16 - ti,hwmods: Must be "mmc<n>", n is controller instance starting 1 16 17 17 18 Optional properties:
+189 -12
drivers/mmc/host/omap_hsmmc.c
··· 29 29 #include <linux/timer.h> 30 30 #include <linux/clk.h> 31 31 #include <linux/of.h> 32 + #include <linux/of_irq.h> 32 33 #include <linux/of_gpio.h> 33 34 #include <linux/of_device.h> 34 35 #include <linux/omap-dmaengine.h> ··· 37 36 #include <linux/mmc/core.h> 38 37 #include <linux/mmc/mmc.h> 39 38 #include <linux/io.h> 39 + #include <linux/irq.h> 40 40 #include <linux/gpio.h> 41 41 #include <linux/regulator/consumer.h> 42 42 #include <linux/pinctrl/consumer.h> ··· 108 106 #define TC_EN (1 << 1) 109 107 #define BWR_EN (1 << 4) 110 108 #define BRR_EN (1 << 5) 109 + #define CIRQ_EN (1 << 8) 111 110 #define ERR_EN (1 << 15) 112 111 #define CTO_EN (1 << 16) 113 112 #define CCRC_EN (1 << 17) ··· 143 140 #define VDD_3V0 3000000 /* 300000 uV */ 144 141 #define VDD_165_195 (ffs(MMC_VDD_165_195) - 1) 145 142 146 - #define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */ 147 143 /* 148 144 * One controller can have multiple slots, like on some omap boards using 149 145 * omap.c controller driver. Luckily this is not currently done on any known ··· 196 194 u32 sysctl; 197 195 u32 capa; 198 196 int irq; 197 + int wake_irq; 199 198 int use_dma, dma_ch; 200 199 struct dma_chan *tx_chan; 201 200 struct dma_chan *rx_chan; ··· 209 206 int req_in_progress; 210 207 unsigned long clk_rate; 211 208 unsigned int flags; 209 + #define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */ 210 + #define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */ 211 + #define HSMMC_WAKE_IRQ_ENABLED (1 << 2) 212 212 struct omap_hsmmc_next next_data; 213 213 struct omap_mmc_platform_data *pdata; 214 214 }; ··· 516 510 static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, 517 511 struct mmc_command *cmd) 518 512 { 519 - unsigned int irq_mask; 513 + u32 irq_mask = INT_EN_MASK; 514 + unsigned long flags; 520 515 521 516 if (host->use_dma) 522 - irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN); 523 - else 524 - irq_mask = INT_EN_MASK; 517 + irq_mask &= ~(BRR_EN | BWR_EN); 525 518 526 519 /* Disable timeout for erases */ 527 520 if (cmd->opcode == MMC_ERASE) 528 521 irq_mask &= ~DTO_EN; 529 522 523 + spin_lock_irqsave(&host->irq_lock, flags); 530 524 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 531 525 OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); 526 + 527 + /* latch pending CIRQ, but don't signal MMC core */ 528 + if (host->flags & HSMMC_SDIO_IRQ_ENABLED) 529 + irq_mask |= CIRQ_EN; 532 530 OMAP_HSMMC_WRITE(host->base, IE, irq_mask); 531 + spin_unlock_irqrestore(&host->irq_lock, flags); 533 532 } 534 533 535 534 static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host) 536 535 { 537 - OMAP_HSMMC_WRITE(host->base, ISE, 0); 538 - OMAP_HSMMC_WRITE(host->base, IE, 0); 536 + u32 irq_mask = 0; 537 + unsigned long flags; 538 + 539 + spin_lock_irqsave(&host->irq_lock, flags); 540 + /* no transfer running but need to keep cirq if enabled */ 541 + if (host->flags & HSMMC_SDIO_IRQ_ENABLED) 542 + irq_mask |= CIRQ_EN; 543 + OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); 544 + OMAP_HSMMC_WRITE(host->base, IE, irq_mask); 539 545 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 546 + spin_unlock_irqrestore(&host->irq_lock, flags); 540 547 } 541 548 542 549 /* Calculate divisor for the given clock frequency */ ··· 700 681 && time_before(jiffies, timeout)) 701 682 ; 702 683 703 - omap_hsmmc_disable_irq(host); 684 + OMAP_HSMMC_WRITE(host->base, ISE, 0); 685 + OMAP_HSMMC_WRITE(host->base, IE, 0); 686 + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 704 687 705 688 /* Do not initialize card-specific things if the power is off */ 706 689 if (host->power_mode == MMC_POWER_OFF) ··· 1139 1118 int status; 1140 1119 1141 1120 status = OMAP_HSMMC_READ(host->base, STAT); 1142 - while (status & INT_EN_MASK && host->req_in_progress) { 1143 - omap_hsmmc_do_irq(host, status); 1121 + while (status & (INT_EN_MASK | CIRQ_EN)) { 1122 + if (host->req_in_progress) 1123 + omap_hsmmc_do_irq(host, status); 1124 + 1125 + if (status & CIRQ_EN) 1126 + mmc_signal_sdio_irq(host->mmc); 1144 1127 1145 1128 /* Flush posted write */ 1146 1129 status = OMAP_HSMMC_READ(host->base, STAT); 1147 1130 } 1131 + 1132 + return IRQ_HANDLED; 1133 + } 1134 + 1135 + static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id) 1136 + { 1137 + struct omap_hsmmc_host *host = dev_id; 1138 + 1139 + /* cirq is level triggered, disable to avoid infinite loop */ 1140 + spin_lock(&host->irq_lock); 1141 + if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { 1142 + disable_irq_nosync(host->wake_irq); 1143 + host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; 1144 + } 1145 + spin_unlock(&host->irq_lock); 1146 + pm_request_resume(host->dev); /* no use counter */ 1148 1147 1149 1148 return IRQ_HANDLED; 1150 1149 } ··· 1680 1639 mmc_slot(host).init_card(card); 1681 1640 } 1682 1641 1642 + static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 1643 + { 1644 + struct omap_hsmmc_host *host = mmc_priv(mmc); 1645 + u32 irq_mask; 1646 + unsigned long flags; 1647 + 1648 + spin_lock_irqsave(&host->irq_lock, flags); 1649 + 1650 + irq_mask = OMAP_HSMMC_READ(host->base, ISE); 1651 + if (enable) { 1652 + host->flags |= HSMMC_SDIO_IRQ_ENABLED; 1653 + irq_mask |= CIRQ_EN; 1654 + } else { 1655 + host->flags &= ~HSMMC_SDIO_IRQ_ENABLED; 1656 + irq_mask &= ~CIRQ_EN; 1657 + } 1658 + OMAP_HSMMC_WRITE(host->base, IE, irq_mask); 1659 + 1660 + /* 1661 + * if enable, piggy back detection on current request 1662 + * but always disable immediately 1663 + */ 1664 + if (!host->req_in_progress || !enable) 1665 + OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); 1666 + 1667 + /* flush posted write */ 1668 + OMAP_HSMMC_READ(host->base, IE); 1669 + 1670 + spin_unlock_irqrestore(&host->irq_lock, flags); 1671 + } 1672 + 1673 + static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) 1674 + { 1675 + struct mmc_host *mmc = host->mmc; 1676 + int ret; 1677 + 1678 + /* 1679 + * For omaps with wake-up path, wakeirq will be irq from pinctrl and 1680 + * for other omaps, wakeirq will be from GPIO (dat line remuxed to 1681 + * gpio). wakeirq is needed to detect sdio irq in runtime suspend state 1682 + * with functional clock disabled. 1683 + */ 1684 + if (!host->dev->of_node || !host->wake_irq) 1685 + return -ENODEV; 1686 + 1687 + /* Prevent auto-enabling of IRQ */ 1688 + irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN); 1689 + ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq, 1690 + IRQF_TRIGGER_LOW | IRQF_ONESHOT, 1691 + mmc_hostname(mmc), host); 1692 + if (ret) { 1693 + dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n"); 1694 + goto err; 1695 + } 1696 + 1697 + /* 1698 + * Some omaps don't have wake-up path from deeper idle states 1699 + * and need to remux SDIO DAT1 to GPIO for wake-up from idle. 1700 + */ 1701 + if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) { 1702 + ret = -ENODEV; 1703 + devm_free_irq(host->dev, host->wake_irq, host); 1704 + goto err; 1705 + } 1706 + 1707 + return 0; 1708 + 1709 + err: 1710 + dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n"); 1711 + host->wake_irq = 0; 1712 + return ret; 1713 + } 1714 + 1683 1715 static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) 1684 1716 { 1685 1717 u32 hctl, capa, value; ··· 1805 1691 .get_cd = omap_hsmmc_get_cd, 1806 1692 .get_ro = omap_hsmmc_get_ro, 1807 1693 .init_card = omap_hsmmc_init_card, 1808 - /* NYET -- enable_sdio_irq */ 1694 + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq, 1809 1695 }; 1810 1696 1811 1697 #ifdef CONFIG_DEBUG_FS ··· 1875 1761 static const struct omap_mmc_of_data omap4_mmc_of_data = { 1876 1762 .reg_offset = 0x100, 1877 1763 }; 1764 + static const struct omap_mmc_of_data am33xx_mmc_of_data = { 1765 + .reg_offset = 0x100, 1766 + .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING, 1767 + }; 1878 1768 1879 1769 static const struct of_device_id omap_mmc_of_match[] = { 1880 1770 { ··· 1894 1776 { 1895 1777 .compatible = "ti,omap4-hsmmc", 1896 1778 .data = &omap4_mmc_of_data, 1779 + }, 1780 + { 1781 + .compatible = "ti,am33xx-hsmmc", 1782 + .data = &am33xx_mmc_of_data, 1897 1783 }, 1898 1784 {}, 1899 1785 }; ··· 2034 1912 host->pbias_enabled = 0; 2035 1913 2036 1914 platform_set_drvdata(pdev, host); 1915 + 1916 + if (pdev->dev.of_node) 1917 + host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1); 2037 1918 2038 1919 mmc->ops = &omap_hsmmc_ops; 2039 1920 ··· 2191 2066 dev_warn(&pdev->dev, 2192 2067 "pins are not configured from the driver\n"); 2193 2068 2069 + /* 2070 + * For now, only support SDIO interrupt if we have a separate 2071 + * wake-up interrupt configured from device tree. This is because 2072 + * the wake-up interrupt is needed for idle state and some 2073 + * platforms need special quirks. And we don't want to add new 2074 + * legacy mux platform init code callbacks any longer as we 2075 + * are moving to DT based booting anyways. 2076 + */ 2077 + ret = omap_hsmmc_configure_wake_irq(host); 2078 + if (!ret) 2079 + mmc->caps |= MMC_CAP_SDIO_IRQ; 2080 + 2194 2081 omap_hsmmc_protect_card(host); 2195 2082 2196 2083 mmc_add_host(mmc); ··· 2307 2170 pm_runtime_get_sync(host->dev); 2308 2171 2309 2172 if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { 2310 - omap_hsmmc_disable_irq(host); 2173 + OMAP_HSMMC_WRITE(host->base, ISE, 0); 2174 + OMAP_HSMMC_WRITE(host->base, IE, 0); 2175 + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 2311 2176 OMAP_HSMMC_WRITE(host->base, HCTL, 2312 2177 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); 2313 2178 } 2179 + 2180 + /* do not wake up due to sdio irq */ 2181 + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && 2182 + !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) 2183 + disable_irq(host->wake_irq); 2314 2184 2315 2185 if (host->dbclk) 2316 2186 clk_disable_unprepare(host->dbclk); ··· 2344 2200 2345 2201 omap_hsmmc_protect_card(host); 2346 2202 2203 + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && 2204 + !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) 2205 + enable_irq(host->wake_irq); 2206 + 2347 2207 pm_runtime_mark_last_busy(host->dev); 2348 2208 pm_runtime_put_autosuspend(host->dev); 2349 2209 return 0; ··· 2363 2215 static int omap_hsmmc_runtime_suspend(struct device *dev) 2364 2216 { 2365 2217 struct omap_hsmmc_host *host; 2218 + unsigned long flags; 2366 2219 2367 2220 host = platform_get_drvdata(to_platform_device(dev)); 2368 2221 omap_hsmmc_context_save(host); 2369 2222 dev_dbg(dev, "disabled\n"); 2370 2223 2224 + spin_lock_irqsave(&host->irq_lock, flags); 2225 + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && 2226 + (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { 2227 + /* disable sdio irq handling to prevent race */ 2228 + OMAP_HSMMC_WRITE(host->base, ISE, 0); 2229 + OMAP_HSMMC_WRITE(host->base, IE, 0); 2230 + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 2231 + 2232 + WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); 2233 + enable_irq(host->wake_irq); 2234 + host->flags |= HSMMC_WAKE_IRQ_ENABLED; 2235 + } 2236 + spin_unlock_irqrestore(&host->irq_lock, flags); 2371 2237 return 0; 2372 2238 } 2373 2239 2374 2240 static int omap_hsmmc_runtime_resume(struct device *dev) 2375 2241 { 2376 2242 struct omap_hsmmc_host *host; 2243 + unsigned long flags; 2377 2244 2378 2245 host = platform_get_drvdata(to_platform_device(dev)); 2379 2246 omap_hsmmc_context_restore(host); 2380 2247 dev_dbg(dev, "enabled\n"); 2381 2248 2249 + spin_lock_irqsave(&host->irq_lock, flags); 2250 + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && 2251 + (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { 2252 + /* sdio irq flag can't change while in runtime suspend */ 2253 + if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { 2254 + disable_irq_nosync(host->wake_irq); 2255 + host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; 2256 + } 2257 + 2258 + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 2259 + OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN); 2260 + OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN); 2261 + } 2262 + spin_unlock_irqrestore(&host->irq_lock, flags); 2382 2263 return 0; 2383 2264 } 2384 2265
+1
include/linux/platform_data/mmc-omap.h
··· 28 28 */ 29 29 #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) 30 30 #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1) 31 + #define OMAP_HSMMC_SWAKEUP_MISSING BIT(2) 31 32 32 33 struct mmc_card; 33 34