diff options
-rw-r--r-- | drivers/mmc/host/sdhci-dove.c | 73 |
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 | ||
32 | struct sdhci_dove_priv { | 33 | struct sdhci_dove_priv { |
33 | struct clk *clk; | 34 | struct clk *clk; |
35 | int gpio_cd; | ||
34 | }; | 36 | }; |
35 | 37 | ||
38 | static 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 | |||
36 | static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) | 46 | static 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 | ||
51 | static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) | 61 | static 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 | ||
169 | err_request_irq: | ||
170 | sdhci_remove_host(host, 0); | ||
117 | err_sdhci_add: | 171 | err_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); |
121 | err_sdhci_pltfm_init: | 175 | err_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 | ||