Merge branch 'sh-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / mmc / host / sdhci-pltfm.c
1 /*
2  * sdhci-pltfm.c Support for SDHCI platform devices
3  * Copyright (c) 2009 Intel Corporation
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* Supports:
20  * SDHCI platform devices
21  *
22  * Inspired by sdhci-pci.c, by Pierre Ossman
23  */
24
25 #include <linux/delay.h>
26 #include <linux/highmem.h>
27 #include <linux/mod_devicetable.h>
28 #include <linux/platform_device.h>
29
30 #include <linux/mmc/host.h>
31
32 #include <linux/io.h>
33 #include <linux/mmc/sdhci-pltfm.h>
34
35 #include "sdhci.h"
36 #include "sdhci-pltfm.h"
37
38 /*****************************************************************************\
39  *                                                                           *
40  * SDHCI core callbacks                                                      *
41  *                                                                           *
42 \*****************************************************************************/
43
44 static struct sdhci_ops sdhci_pltfm_ops = {
45 };
46
47 /*****************************************************************************\
48  *                                                                           *
49  * Device probing/removal                                                    *
50  *                                                                           *
51 \*****************************************************************************/
52
53 static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
54 {
55         const struct platform_device_id *platid = platform_get_device_id(pdev);
56         struct sdhci_pltfm_data *pdata;
57         struct sdhci_host *host;
58         struct sdhci_pltfm_host *pltfm_host;
59         struct resource *iomem;
60         int ret;
61
62         if (platid && platid->driver_data)
63                 pdata = (void *)platid->driver_data;
64         else
65                 pdata = pdev->dev.platform_data;
66
67         iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
68         if (!iomem) {
69                 ret = -ENOMEM;
70                 goto err;
71         }
72
73         if (resource_size(iomem) < 0x100)
74                 dev_err(&pdev->dev, "Invalid iomem size. You may "
75                         "experience problems.\n");
76
77         /* Some PCI-based MFD need the parent here */
78         if (pdev->dev.parent != &platform_bus)
79                 host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
80         else
81                 host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
82
83         if (IS_ERR(host)) {
84                 ret = PTR_ERR(host);
85                 goto err;
86         }
87
88         pltfm_host = sdhci_priv(host);
89
90         host->hw_name = "platform";
91         if (pdata && pdata->ops)
92                 host->ops = pdata->ops;
93         else
94                 host->ops = &sdhci_pltfm_ops;
95         if (pdata)
96                 host->quirks = pdata->quirks;
97         host->irq = platform_get_irq(pdev, 0);
98
99         if (!request_mem_region(iomem->start, resource_size(iomem),
100                 mmc_hostname(host->mmc))) {
101                 dev_err(&pdev->dev, "cannot request region\n");
102                 ret = -EBUSY;
103                 goto err_request;
104         }
105
106         host->ioaddr = ioremap(iomem->start, resource_size(iomem));
107         if (!host->ioaddr) {
108                 dev_err(&pdev->dev, "failed to remap registers\n");
109                 ret = -ENOMEM;
110                 goto err_remap;
111         }
112
113         if (pdata && pdata->init) {
114                 ret = pdata->init(host, pdata);
115                 if (ret)
116                         goto err_plat_init;
117         }
118
119         ret = sdhci_add_host(host);
120         if (ret)
121                 goto err_add_host;
122
123         platform_set_drvdata(pdev, host);
124
125         return 0;
126
127 err_add_host:
128         if (pdata && pdata->exit)
129                 pdata->exit(host);
130 err_plat_init:
131         iounmap(host->ioaddr);
132 err_remap:
133         release_mem_region(iomem->start, resource_size(iomem));
134 err_request:
135         sdhci_free_host(host);
136 err:
137         printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret);
138         return ret;
139 }
140
141 static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
142 {
143         struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
144         struct sdhci_host *host = platform_get_drvdata(pdev);
145         struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
146         int dead;
147         u32 scratch;
148
149         dead = 0;
150         scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
151         if (scratch == (u32)-1)
152                 dead = 1;
153
154         sdhci_remove_host(host, dead);
155         if (pdata && pdata->exit)
156                 pdata->exit(host);
157         iounmap(host->ioaddr);
158         release_mem_region(iomem->start, resource_size(iomem));
159         sdhci_free_host(host);
160         platform_set_drvdata(pdev, NULL);
161
162         return 0;
163 }
164
165 static const struct platform_device_id sdhci_pltfm_ids[] = {
166         { "sdhci", },
167 #ifdef CONFIG_MMC_SDHCI_CNS3XXX
168         { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata },
169 #endif
170 #ifdef CONFIG_MMC_SDHCI_ESDHC_IMX
171         { "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata },
172 #endif
173 #ifdef CONFIG_MMC_SDHCI_DOVE
174         { "sdhci-dove", (kernel_ulong_t)&sdhci_dove_pdata },
175 #endif
176 #ifdef CONFIG_MMC_SDHCI_TEGRA
177         { "sdhci-tegra", (kernel_ulong_t)&sdhci_tegra_pdata },
178 #endif
179         { },
180 };
181 MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
182
183 #ifdef CONFIG_PM
184 static int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
185 {
186         struct sdhci_host *host = platform_get_drvdata(dev);
187
188         return sdhci_suspend_host(host, state);
189 }
190
191 static int sdhci_pltfm_resume(struct platform_device *dev)
192 {
193         struct sdhci_host *host = platform_get_drvdata(dev);
194
195         return sdhci_resume_host(host);
196 }
197 #else
198 #define sdhci_pltfm_suspend     NULL
199 #define sdhci_pltfm_resume      NULL
200 #endif  /* CONFIG_PM */
201
202 static struct platform_driver sdhci_pltfm_driver = {
203         .driver = {
204                 .name   = "sdhci",
205                 .owner  = THIS_MODULE,
206         },
207         .probe          = sdhci_pltfm_probe,
208         .remove         = __devexit_p(sdhci_pltfm_remove),
209         .id_table       = sdhci_pltfm_ids,
210         .suspend        = sdhci_pltfm_suspend,
211         .resume         = sdhci_pltfm_resume,
212 };
213
214 /*****************************************************************************\
215  *                                                                           *
216  * Driver init/exit                                                          *
217  *                                                                           *
218 \*****************************************************************************/
219
220 static int __init sdhci_drv_init(void)
221 {
222         return platform_driver_register(&sdhci_pltfm_driver);
223 }
224
225 static void __exit sdhci_drv_exit(void)
226 {
227         platform_driver_unregister(&sdhci_pltfm_driver);
228 }
229
230 module_init(sdhci_drv_init);
231 module_exit(sdhci_drv_exit);
232
233 MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
234 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
235 MODULE_LICENSE("GPL v2");