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

mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x

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>
Acked-by: Balaji T K <balajitk@ti.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Andreas Fenkart and committed by
Ulf Hansson
455e5cd6 97978a44

+74 -3
+53
Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
··· 57 57 &edma 25>; 58 58 dma-names = "tx", "rx"; 59 59 }; 60 + 61 + [workaround for missing swakeup on am33xx] 62 + 63 + This SOC is missing the swakeup line, it will not detect SDIO irq 64 + while in suspend. 65 + 66 + ------ 67 + | PRCM | 68 + ------ 69 + ^ | 70 + swakeup | | fclk 71 + | v 72 + ------ ------- ----- 73 + | card | -- CIRQ --> | hsmmc | -- IRQ --> | CPU | 74 + ------ ------- ----- 75 + 76 + In suspend the fclk is off and the module is disfunctional. Even register reads 77 + will fail. A small logic in the host will request fclk restore, when an 78 + external event is detected. Once the clock is restored, the host detects the 79 + event normally. Since am33xx doesn't have this line it never wakes from 80 + suspend. 81 + 82 + The workaround is to reconfigure the dat1 line as a GPIO upon suspend. To make 83 + this work, we need to set the named pinctrl states "default" and "idle". 84 + Prepare idle to remux dat1 as a gpio, and default to remux it back as sdio 85 + dat1. The MMC driver will then toggle between idle and default state during 86 + runtime. 87 + 88 + In summary: 89 + 1. select matching 'compatible' section, see example below. 90 + 2. specify pinctrl states "default" and "idle", "sleep" is optional. 91 + 3. specify the gpio irq used for detecting sdio irq in suspend 92 + 93 + If configuration is incomplete, a warning message is emitted "falling back to 94 + polling". Also check the "sdio irq mode" in /sys/kernel/debug/mmc0/regs. Mind 95 + not every application needs SDIO irq, e.g. MMC cards. 96 + 97 + mmc1: mmc@48060100 { 98 + compatible = "ti,am33xx-hsmmc"; 99 + ... 100 + pinctrl-names = "default", "idle", "sleep" 101 + pinctrl-0 = <&mmc1_pins>; 102 + pinctrl-1 = <&mmc1_idle>; 103 + pinctrl-2 = <&mmc1_sleep>; 104 + ... 105 + interrupts-extended = <&intc 64 &gpio2 28 0>; 106 + }; 107 + 108 + mmc1_idle : pinmux_cirq_pin { 109 + pinctrl-single,pins = < 110 + 0x0f8 0x3f /* GPIO2_28 */ 111 + >; 112 + };
+21 -3
drivers/mmc/host/omap_hsmmc.c
··· 1754 1754 * and need to remux SDIO DAT1 to GPIO for wake-up from idle. 1755 1755 */ 1756 1756 if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) { 1757 - ret = -ENODEV; 1758 - devm_free_irq(host->dev, host->wake_irq, host); 1759 - goto err; 1757 + struct pinctrl *p = devm_pinctrl_get(host->dev); 1758 + if (!p) { 1759 + ret = -ENODEV; 1760 + goto err_free_irq; 1761 + } 1762 + if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT))) { 1763 + dev_info(host->dev, "missing default pinctrl state\n"); 1764 + devm_pinctrl_put(p); 1765 + ret = -EINVAL; 1766 + goto err_free_irq; 1767 + } 1768 + 1769 + if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_IDLE))) { 1770 + dev_info(host->dev, "missing idle pinctrl state\n"); 1771 + devm_pinctrl_put(p); 1772 + ret = -EINVAL; 1773 + goto err_free_irq; 1774 + } 1775 + devm_pinctrl_put(p); 1760 1776 } 1761 1777 1762 1778 OMAP_HSMMC_WRITE(host->base, HCTL, 1763 1779 OMAP_HSMMC_READ(host->base, HCTL) | IWE); 1764 1780 return 0; 1765 1781 1782 + err_free_irq: 1783 + devm_free_irq(host->dev, host->wake_irq, host); 1766 1784 err: 1767 1785 dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n"); 1768 1786 host->wake_irq = 0;