]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mmc/host/sdhci-tegra.c
Merge remote-tracking branch 'mmc/mmc-next'
[karo-tx-linux.git] / drivers / mmc / host / sdhci-tegra.c
1 /*
2  * Copyright (C) 2010 Google, Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14
15 #include <linux/err.h>
16 #include <linux/init.h>
17 #include <linux/platform_device.h>
18 #include <linux/clk.h>
19 #include <linux/io.h>
20 #include <linux/of.h>
21 #include <linux/of_gpio.h>
22 #include <linux/gpio.h>
23 #include <linux/mmc/card.h>
24 #include <linux/mmc/host.h>
25
26 #include <asm/gpio.h>
27 #include <mach/sdhci.h>
28
29 #include "sdhci-pltfm.h"
30
31 static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
32 {
33         u32 val;
34
35         if (unlikely(reg == SDHCI_PRESENT_STATE)) {
36                 /* Use wp_gpio here instead? */
37                 val = readl(host->ioaddr + reg);
38                 return val | SDHCI_WRITE_PROTECT;
39         }
40
41         return readl(host->ioaddr + reg);
42 }
43
44 static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
45 {
46         if (unlikely(reg == SDHCI_HOST_VERSION)) {
47                 /* Erratum: Version register is invalid in HW. */
48                 return SDHCI_SPEC_200;
49         }
50
51         return readw(host->ioaddr + reg);
52 }
53
54 static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
55 {
56         /* Seems like we're getting spurious timeout and crc errors, so
57          * disable signalling of them. In case of real errors software
58          * timers should take care of eventually detecting them.
59          */
60         if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
61                 val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
62
63         writel(val, host->ioaddr + reg);
64
65         if (unlikely(reg == SDHCI_INT_ENABLE)) {
66                 /* Erratum: Must enable block gap interrupt detection */
67                 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
68                 if (val & SDHCI_INT_CARD_INT)
69                         gap_ctrl |= 0x8;
70                 else
71                         gap_ctrl &= ~0x8;
72                 writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
73         }
74 }
75
76 static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
77 {
78         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
79         struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
80
81         if (!gpio_is_valid(plat->wp_gpio))
82                 return -1;
83
84         return gpio_get_value(plat->wp_gpio);
85 }
86
87 static irqreturn_t carddetect_irq(int irq, void *data)
88 {
89         struct sdhci_host *sdhost = (struct sdhci_host *)data;
90
91         tasklet_schedule(&sdhost->card_tasklet);
92         return IRQ_HANDLED;
93 };
94
95 static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
96 {
97         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
98         struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
99         u32 ctrl;
100
101         ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
102         if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
103                 ctrl &= ~SDHCI_CTRL_4BITBUS;
104                 ctrl |= SDHCI_CTRL_8BITBUS;
105         } else {
106                 ctrl &= ~SDHCI_CTRL_8BITBUS;
107                 if (bus_width == MMC_BUS_WIDTH_4)
108                         ctrl |= SDHCI_CTRL_4BITBUS;
109                 else
110                         ctrl &= ~SDHCI_CTRL_4BITBUS;
111         }
112         sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
113         return 0;
114 }
115
116 static struct sdhci_ops tegra_sdhci_ops = {
117         .get_ro     = tegra_sdhci_get_ro,
118         .read_l     = tegra_sdhci_readl,
119         .read_w     = tegra_sdhci_readw,
120         .write_l    = tegra_sdhci_writel,
121         .platform_8bit_width = tegra_sdhci_8bit,
122 };
123
124 static struct sdhci_pltfm_data sdhci_tegra_pdata = {
125         .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
126                   SDHCI_QUIRK_SINGLE_POWER_WRITE |
127                   SDHCI_QUIRK_NO_HISPD_BIT |
128                   SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
129         .ops  = &tegra_sdhci_ops,
130 };
131
132 static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
133         { .compatible = "nvidia,tegra20-sdhci", },
134         {}
135 };
136 MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
137
138 static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
139                                                 struct platform_device *pdev)
140 {
141         struct tegra_sdhci_platform_data *plat;
142         struct device_node *np = pdev->dev.of_node;
143
144         if (!np)
145                 return NULL;
146
147         plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
148         if (!plat) {
149                 dev_err(&pdev->dev, "Can't allocate platform data\n");
150                 return NULL;
151         }
152
153         plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
154         plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
155         plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
156         if (of_find_property(np, "support-8bit", NULL))
157                 plat->is_8bit = 1;
158
159         return plat;
160 }
161
162 static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
163 {
164         struct sdhci_pltfm_host *pltfm_host;
165         struct tegra_sdhci_platform_data *plat;
166         struct sdhci_host *host;
167         struct clk *clk;
168         int rc;
169
170         host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata);
171         if (IS_ERR(host))
172                 return PTR_ERR(host);
173
174         pltfm_host = sdhci_priv(host);
175
176         plat = pdev->dev.platform_data;
177
178         if (plat == NULL)
179                 plat = sdhci_tegra_dt_parse_pdata(pdev);
180
181         if (plat == NULL) {
182                 dev_err(mmc_dev(host->mmc), "missing platform data\n");
183                 rc = -ENXIO;
184                 goto err_no_plat;
185         }
186
187         pltfm_host->priv = plat;
188
189         if (gpio_is_valid(plat->power_gpio)) {
190                 rc = gpio_request(plat->power_gpio, "sdhci_power");
191                 if (rc) {
192                         dev_err(mmc_dev(host->mmc),
193                                 "failed to allocate power gpio\n");
194                         goto err_power_req;
195                 }
196                 tegra_gpio_enable(plat->power_gpio);
197                 gpio_direction_output(plat->power_gpio, 1);
198         }
199
200         if (gpio_is_valid(plat->cd_gpio)) {
201                 rc = gpio_request(plat->cd_gpio, "sdhci_cd");
202                 if (rc) {
203                         dev_err(mmc_dev(host->mmc),
204                                 "failed to allocate cd gpio\n");
205                         goto err_cd_req;
206                 }
207                 tegra_gpio_enable(plat->cd_gpio);
208                 gpio_direction_input(plat->cd_gpio);
209
210                 rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
211                                  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
212                                  mmc_hostname(host->mmc), host);
213
214                 if (rc) {
215                         dev_err(mmc_dev(host->mmc), "request irq error\n");
216                         goto err_cd_irq_req;
217                 }
218
219         }
220
221         if (gpio_is_valid(plat->wp_gpio)) {
222                 rc = gpio_request(plat->wp_gpio, "sdhci_wp");
223                 if (rc) {
224                         dev_err(mmc_dev(host->mmc),
225                                 "failed to allocate wp gpio\n");
226                         goto err_wp_req;
227                 }
228                 tegra_gpio_enable(plat->wp_gpio);
229                 gpio_direction_input(plat->wp_gpio);
230         }
231
232         clk = clk_get(mmc_dev(host->mmc), NULL);
233         if (IS_ERR(clk)) {
234                 dev_err(mmc_dev(host->mmc), "clk err\n");
235                 rc = PTR_ERR(clk);
236                 goto err_clk_get;
237         }
238         clk_enable(clk);
239         pltfm_host->clk = clk;
240
241         host->mmc->pm_caps = plat->pm_flags;
242
243         if (plat->is_8bit)
244                 host->mmc->caps |= MMC_CAP_8_BIT_DATA;
245
246         rc = sdhci_add_host(host);
247         if (rc)
248                 goto err_add_host;
249
250         return 0;
251
252 err_add_host:
253         clk_disable(pltfm_host->clk);
254         clk_put(pltfm_host->clk);
255 err_clk_get:
256         if (gpio_is_valid(plat->wp_gpio)) {
257                 tegra_gpio_disable(plat->wp_gpio);
258                 gpio_free(plat->wp_gpio);
259         }
260 err_wp_req:
261         if (gpio_is_valid(plat->cd_gpio))
262                 free_irq(gpio_to_irq(plat->cd_gpio), host);
263 err_cd_irq_req:
264         if (gpio_is_valid(plat->cd_gpio)) {
265                 tegra_gpio_disable(plat->cd_gpio);
266                 gpio_free(plat->cd_gpio);
267         }
268 err_cd_req:
269         if (gpio_is_valid(plat->power_gpio)) {
270                 tegra_gpio_disable(plat->power_gpio);
271                 gpio_free(plat->power_gpio);
272         }
273 err_power_req:
274 err_no_plat:
275         sdhci_pltfm_free(pdev);
276         return rc;
277 }
278
279 static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
280 {
281         struct sdhci_host *host = platform_get_drvdata(pdev);
282         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
283         struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
284         int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
285
286         sdhci_remove_host(host, dead);
287
288         if (gpio_is_valid(plat->wp_gpio)) {
289                 tegra_gpio_disable(plat->wp_gpio);
290                 gpio_free(plat->wp_gpio);
291         }
292
293         if (gpio_is_valid(plat->cd_gpio)) {
294                 free_irq(gpio_to_irq(plat->cd_gpio), host);
295                 tegra_gpio_disable(plat->cd_gpio);
296                 gpio_free(plat->cd_gpio);
297         }
298
299         if (gpio_is_valid(plat->power_gpio)) {
300                 tegra_gpio_disable(plat->power_gpio);
301                 gpio_free(plat->power_gpio);
302         }
303
304         clk_disable(pltfm_host->clk);
305         clk_put(pltfm_host->clk);
306
307         sdhci_pltfm_free(pdev);
308
309         return 0;
310 }
311
312 static struct platform_driver sdhci_tegra_driver = {
313         .driver         = {
314                 .name   = "sdhci-tegra",
315                 .owner  = THIS_MODULE,
316                 .of_match_table = sdhci_tegra_dt_match,
317         },
318         .probe          = sdhci_tegra_probe,
319         .remove         = __devexit_p(sdhci_tegra_remove),
320 #ifdef CONFIG_PM
321         .suspend        = sdhci_pltfm_suspend,
322         .resume         = sdhci_pltfm_resume,
323 #endif
324 };
325
326 static int __init sdhci_tegra_init(void)
327 {
328         return platform_driver_register(&sdhci_tegra_driver);
329 }
330 module_init(sdhci_tegra_init);
331
332 static void __exit sdhci_tegra_exit(void)
333 {
334         platform_driver_unregister(&sdhci_tegra_driver);
335 }
336 module_exit(sdhci_tegra_exit);
337
338 MODULE_DESCRIPTION("SDHCI driver for Tegra");
339 MODULE_AUTHOR(" Google, Inc.");
340 MODULE_LICENSE("GPL v2");