aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-11-22 18:55:51 -0500
committerChris Ball <cjb@laptop.org>2012-12-06 13:55:14 -0500
commitf8ec589b86f66d0e13a262b6e8b589b03dde5173 (patch)
tree02387f2ac18412ca5e388f81690175f4bbd5749d /drivers/mmc
parentc430689f3f0f73e1aadaaf6cfd39930be1e73da1 (diff)
mmc: sdhci-dove: allow GPIOs to be used for card detection on Dove
This commit taken from Rabeeh's Cubox kernel and re-worked for DT; Sebastian Hasselbrath is believed to be the original author. Some Cuboxes require a GPIO for card detection; this implements the optional GPIO support for card detection. This GPIO is logic 0 for card inserted. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-dove.c73
1 files changed, 67 insertions, 6 deletions
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 4af64f32c6d1..e6214480bc98 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -19,20 +19,30 @@
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */ 20 */
21 21
22#include <linux/err.h>
23#include <linux/io.h>
24#include <linux/clk.h> 22#include <linux/clk.h>
25#include <linux/err.h> 23#include <linux/err.h>
26#include <linux/module.h> 24#include <linux/gpio.h>
25#include <linux/io.h>
27#include <linux/mmc/host.h> 26#include <linux/mmc/host.h>
27#include <linux/module.h>
28#include <linux/of.h> 28#include <linux/of.h>
29#include <linux/of_gpio.h>
29 30
30#include "sdhci-pltfm.h" 31#include "sdhci-pltfm.h"
31 32
32struct sdhci_dove_priv { 33struct sdhci_dove_priv {
33 struct clk *clk; 34 struct clk *clk;
35 int gpio_cd;
34}; 36};
35 37
38static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data)
39{
40 struct sdhci_host *host = data;
41
42 tasklet_schedule(&host->card_tasklet);
43 return IRQ_HANDLED;
44}
45
36static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) 46static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
37{ 47{
38 u16 ret; 48 u16 ret;
@@ -50,16 +60,25 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
50 60
51static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) 61static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
52{ 62{
63 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
64 struct sdhci_dove_priv *priv = pltfm_host->priv;
53 u32 ret; 65 u32 ret;
54 66
67 ret = readl(host->ioaddr + reg);
68
55 switch (reg) { 69 switch (reg) {
56 case SDHCI_CAPABILITIES: 70 case SDHCI_CAPABILITIES:
57 ret = readl(host->ioaddr + reg);
58 /* Mask the support for 3.0V */ 71 /* Mask the support for 3.0V */
59 ret &= ~SDHCI_CAN_VDD_300; 72 ret &= ~SDHCI_CAN_VDD_300;
60 break; 73 break;
61 default: 74 case SDHCI_PRESENT_STATE:
62 ret = readl(host->ioaddr + reg); 75 if (gpio_is_valid(priv->gpio_cd)) {
76 if (gpio_get_value(priv->gpio_cd) == 0)
77 ret |= SDHCI_CARD_PRESENT;
78 else
79 ret &= ~SDHCI_CARD_PRESENT;
80 }
81 break;
63 } 82 }
64 return ret; 83 return ret;
65} 84}
@@ -94,6 +113,23 @@ static int __devinit sdhci_dove_probe(struct platform_device *pdev)
94 113
95 priv->clk = devm_clk_get(&pdev->dev, NULL); 114 priv->clk = devm_clk_get(&pdev->dev, NULL);
96 115
116 if (pdev->dev.of_node) {
117 priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
118 "cd-gpios", 0);
119 } else {
120 priv->gpio_cd = -EINVAL;
121 }
122
123 if (gpio_is_valid(priv->gpio_cd)) {
124 ret = gpio_request(priv->gpio_cd, "sdhci-cd");
125 if (ret) {
126 dev_err(&pdev->dev, "card detect gpio request failed: %d\n",
127 ret);
128 return ret;
129 }
130 gpio_direction_input(priv->gpio_cd);
131 }
132
97 host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata); 133 host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata);
98 if (IS_ERR(host)) { 134 if (IS_ERR(host)) {
99 ret = PTR_ERR(host); 135 ret = PTR_ERR(host);
@@ -112,13 +148,33 @@ static int __devinit sdhci_dove_probe(struct platform_device *pdev)
112 if (ret) 148 if (ret)
113 goto err_sdhci_add; 149 goto err_sdhci_add;
114 150
151 /*
152 * We must request the IRQ after sdhci_add_host(), as the tasklet only
153 * gets setup in sdhci_add_host() and we oops.
154 */
155 if (gpio_is_valid(priv->gpio_cd)) {
156 ret = request_irq(gpio_to_irq(priv->gpio_cd),
157 sdhci_dove_carddetect_irq,
158 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
159 mmc_hostname(host->mmc), host);
160 if (ret) {
161 dev_err(&pdev->dev, "card detect irq request failed: %d\n",
162 ret);
163 goto err_request_irq;
164 }
165 }
166
115 return 0; 167 return 0;
116 168
169err_request_irq:
170 sdhci_remove_host(host, 0);
117err_sdhci_add: 171err_sdhci_add:
118 if (!IS_ERR(priv->clk)) 172 if (!IS_ERR(priv->clk))
119 clk_disable_unprepare(priv->clk); 173 clk_disable_unprepare(priv->clk);
120 sdhci_pltfm_free(pdev); 174 sdhci_pltfm_free(pdev);
121err_sdhci_pltfm_init: 175err_sdhci_pltfm_init:
176 if (gpio_is_valid(priv->gpio_cd))
177 gpio_free(priv->gpio_cd);
122 return ret; 178 return ret;
123} 179}
124 180
@@ -130,6 +186,11 @@ static int __devexit sdhci_dove_remove(struct platform_device *pdev)
130 186
131 sdhci_pltfm_unregister(pdev); 187 sdhci_pltfm_unregister(pdev);
132 188
189 if (gpio_is_valid(priv->gpio_cd)) {
190 free_irq(gpio_to_irq(priv->gpio_cd), host);
191 gpio_free(priv->gpio_cd);
192 }
193
133 if (!IS_ERR(priv->clk)) 194 if (!IS_ERR(priv->clk))
134 clk_disable_unprepare(priv->clk); 195 clk_disable_unprepare(priv->clk);
135 196