Merge remote-tracking branch 'remotes/tegra/android-tegra-2.6.36' into develop-2...
[firefly-linux-kernel-4.4.55.git] / drivers / mmc / host / sdhci-tegra.c
1 /*
2  * drivers/mmc/host/sdhci-tegra.c
3  *
4  * Copyright (C) 2009 Palm, Inc.
5  * Author: Yvonne Yip <y@palm.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <linux/err.h>
19 #include <linux/init.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/clk.h>
23 #include <linux/io.h>
24 #include <linux/gpio.h>
25 #include <linux/mmc/card.h>
26
27 #include <mach/sdhci.h>
28
29 #include "sdhci.h"
30
31 #define DRIVER_NAME    "sdhci-tegra"
32
33 #define SDHCI_VENDOR_CLOCK_CNTRL       0x100
34
35 struct tegra_sdhci_host {
36         struct sdhci_host *sdhci;
37         struct clk *clk;
38         struct tegra_sdhci_platform_data *plat;
39         int clk_enabled;
40 };
41
42 static irqreturn_t carddetect_irq(int irq, void *data)
43 {
44         struct sdhci_host *sdhost = (struct sdhci_host *)data;
45
46         sdhci_card_detect_callback(sdhost);
47         return IRQ_HANDLED;
48 };
49
50 static void sdhci_status_notify_cb(int card_present, void *dev_id)
51 {
52         struct sdhci_host *host = (struct sdhci_host *)dev_id;
53         struct tegra_sdhci_host *tegra_host = sdhci_priv(host);
54         unsigned int status, oldstat;
55
56         pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc),
57                 card_present);
58
59         if (!tegra_host->plat->mmc_data.status) {
60                 mmc_detect_change(host->mmc, 0);
61                 return;
62         }
63
64         status = tegra_host->plat->mmc_data.status(mmc_dev(host->mmc));
65
66         oldstat = host->card_present;
67         host->card_present = status;
68         if (status ^ oldstat) {
69                 pr_debug("%s: Slot status change detected (%d -> %d)\n",
70                         mmc_hostname(host->mmc), oldstat, status);
71                 if (status && !tegra_host->plat->mmc_data.built_in)
72                         mmc_detect_change(host->mmc, (5 * HZ) / 2);
73                 else
74                         mmc_detect_change(host->mmc, 0);
75         }
76 }
77
78 static int tegra_sdhci_enable_dma(struct sdhci_host *host)
79 {
80         return 0;
81 }
82
83 static void tegra_sdhci_enable_clock(struct tegra_sdhci_host *host, int enable)
84 {
85         if (enable && !host->clk_enabled) {
86                 clk_enable(host->clk);
87                 sdhci_writeb(host->sdhci, 1, SDHCI_VENDOR_CLOCK_CNTRL);
88                 host->clk_enabled = 1;
89         } else if (!enable && host->clk_enabled) {
90                 sdhci_writeb(host->sdhci, 0, SDHCI_VENDOR_CLOCK_CNTRL);
91                 clk_disable(host->clk);
92                 host->clk_enabled = 0;
93         }
94 }
95
96 static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
97 {
98         struct tegra_sdhci_host *host = sdhci_priv(sdhci);
99         pr_debug("tegra sdhci clock %s %u enabled=%d\n",
100                 mmc_hostname(sdhci->mmc), clock, host->clk_enabled);
101
102         tegra_sdhci_enable_clock(host, clock);
103 }
104
105 static struct sdhci_ops tegra_sdhci_ops = {
106         .enable_dma = tegra_sdhci_enable_dma,
107         .set_clock = tegra_sdhci_set_clock,
108 };
109
110 static int __devinit tegra_sdhci_probe(struct platform_device *pdev)
111 {
112         int rc;
113         struct tegra_sdhci_platform_data *plat;
114         struct sdhci_host *sdhci;
115         struct tegra_sdhci_host *host;
116         struct resource *res;
117         int irq;
118         void __iomem *ioaddr;
119
120         plat = pdev->dev.platform_data;
121         if (plat == NULL)
122                 return -ENXIO;
123
124         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
125         if (res == NULL)
126                 return -ENODEV;
127
128         irq = res->start;
129
130         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
131         if (res == NULL)
132                 return -ENODEV;
133
134         ioaddr = ioremap(res->start, res->end - res->start);
135
136         sdhci = sdhci_alloc_host(&pdev->dev, sizeof(struct tegra_sdhci_host));
137         if (IS_ERR(sdhci)) {
138                 rc = PTR_ERR(sdhci);
139                 goto err_unmap;
140         }
141
142         host = sdhci_priv(sdhci);
143         host->sdhci = sdhci;
144         host->plat = plat;
145
146 #ifdef CONFIG_MMC_EMBEDDED_SDIO
147         if (plat->mmc_data.embedded_sdio)
148                 mmc_set_embedded_sdio_data(sdhci->mmc,
149                                 &plat->mmc_data.embedded_sdio->cis,
150                                 &plat->mmc_data.embedded_sdio->cccr,
151                                 plat->mmc_data.embedded_sdio->funcs,
152                                 plat->mmc_data.embedded_sdio->num_funcs);
153 #endif
154
155         host->clk = clk_get(&pdev->dev, plat->clk_id);
156         if (IS_ERR(host->clk)) {
157                 rc = PTR_ERR(host->clk);
158                 goto err_free_host;
159         }
160
161         rc = clk_enable(host->clk);
162         if (rc != 0)
163                 goto err_clkput;
164
165         host->clk_enabled = 1;
166         sdhci->hw_name = "tegra";
167         sdhci->ops = &tegra_sdhci_ops;
168         sdhci->irq = irq;
169         sdhci->ioaddr = ioaddr;
170         sdhci->version = SDHCI_SPEC_200;
171         sdhci->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
172                         SDHCI_QUIRK_SINGLE_POWER_WRITE |
173                         SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP |
174                         SDHCI_QUIRK_BROKEN_WRITE_PROTECT |
175                         SDHCI_QUIRK_BROKEN_CTRL_HISPD |
176                         SDHCI_QUIRK_NO_HISPD_BIT |
177                         SDHCI_QUIRK_8_BIT_DATA |
178                         SDHCI_QUIRK_NO_VERSION_REG |
179                         SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
180                         SDHCI_QUIRK_NO_SDIO_IRQ;
181
182         if (plat->force_hs != 0)
183                 sdhci->quirks |= SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE;
184
185         sdhci->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
186         if (plat->mmc_data.built_in)
187                 sdhci->mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
188
189         if (plat->rt_disable != 0)
190                 sdhci->quirks |= SDHCI_QUIRK_RUNTIME_DISABLE;
191
192         rc = sdhci_add_host(sdhci);
193         if (rc)
194                 goto err_clk_disable;
195
196         platform_set_drvdata(pdev, host);
197
198         if (plat->cd_gpio != -1) {
199                 rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
200                         IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
201                         mmc_hostname(sdhci->mmc), sdhci);
202
203                 if (rc)
204                         goto err_remove_host;
205         } else if (plat->mmc_data.register_status_notify) {
206                 plat->mmc_data.register_status_notify(sdhci_status_notify_cb, sdhci);
207         }
208
209         if (plat->mmc_data.status) {
210                 sdhci->card_present = host->plat->mmc_data.status(mmc_dev(sdhci->mmc));
211         }
212
213         if (plat->board_probe)
214                 plat->board_probe(pdev->id, sdhci->mmc);
215
216         printk(KERN_INFO "sdhci%d: initialized irq %d ioaddr %p\n", pdev->id,
217                         sdhci->irq, sdhci->ioaddr);
218
219         return 0;
220
221 err_remove_host:
222         sdhci_remove_host(sdhci, 1);
223 err_clk_disable:
224         clk_disable(host->clk);
225 err_clkput:
226         clk_put(host->clk);
227 err_free_host:
228         if (sdhci)
229                 sdhci_free_host(sdhci);
230 err_unmap:
231         iounmap(sdhci->ioaddr);
232
233         return rc;
234 }
235
236 static int tegra_sdhci_remove(struct platform_device *pdev)
237 {
238         struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
239         if (host) {
240                 struct tegra_sdhci_platform_data *plat;
241                 plat = pdev->dev.platform_data;
242                 if (plat && plat->board_probe)
243                         plat->board_probe(pdev->id, host->sdhci->mmc);
244
245                 sdhci_remove_host(host->sdhci, 0);
246                 sdhci_free_host(host->sdhci);
247         }
248         return 0;
249 }
250
251 #ifdef CONFIG_PM
252 static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state)
253 {
254         struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
255         struct mmc_host *mmc = host->sdhci->mmc;
256         int ret;
257
258         if (host->plat->mmc_data.built_in)
259                 mmc->pm_flags |= MMC_PM_KEEP_POWER;
260
261         ret = sdhci_suspend_host(host->sdhci, state);
262         if (ret)
263                 pr_err("%s: failed, error = %d\n", __func__, ret);
264
265         tegra_sdhci_enable_clock(host, 0);
266         return ret;
267 }
268
269 static int tegra_sdhci_resume(struct platform_device *pdev)
270 {
271         struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
272         int ret;
273
274         tegra_sdhci_enable_clock(host, 1);
275         ret = sdhci_resume_host(host->sdhci);
276         if (ret)
277                 pr_err("%s: failed, error = %d\n", __func__, ret);
278
279         return ret;
280 }
281 #else
282 #define tegra_sdhci_suspend    NULL
283 #define tegra_sdhci_resume     NULL
284 #endif
285
286 static struct platform_driver tegra_sdhci_driver = {
287         .probe = tegra_sdhci_probe,
288         .remove = tegra_sdhci_remove,
289         .suspend = tegra_sdhci_suspend,
290         .resume = tegra_sdhci_resume,
291         .driver = {
292                 .name = DRIVER_NAME,
293                 .owner = THIS_MODULE,
294         },
295 };
296
297 static int __init tegra_sdhci_init(void)
298 {
299         return platform_driver_register(&tegra_sdhci_driver);
300 }
301
302 static void __exit tegra_sdhci_exit(void)
303 {
304         platform_driver_unregister(&tegra_sdhci_driver);
305 }
306
307 module_init(tegra_sdhci_init);
308 module_exit(tegra_sdhci_exit);
309
310 MODULE_DESCRIPTION("Tegra SDHCI controller driver");
311 MODULE_LICENSE("GPL");