aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorShawn Guo <shawn.guo@linaro.org>2011-05-27 11:48:12 -0400
committerChris Ball <cjb@laptop.org>2011-07-20 17:16:06 -0400
commit85d6509dc8ca24b2b652863ef7a75622ddca17d6 (patch)
treee564c2d4f80478027abc96cb7d87da952b38409e /drivers/mmc/host
parent3a5c3743f15f27237ab025736a981e2d0c9fdfed (diff)
mmc: sdhci: make sdhci-pltfm device drivers self registered
The patch turns the common stuff in sdhci-pltfm.c into functions, and add device drivers their own .probe and .remove which in turn call into the common functions, so that those sdhci-pltfm device drivers register itself and keep all device specific things away from common sdhci-pltfm file. Signed-off-by: Shawn Guo <shawn.guo@linaro.org> Reviewed-by: Grant Likely <grant.likely@secretlab.ca> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Anton Vorontsov <cbouatmailru@gmail.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Kconfig29
-rw-r--r--drivers/mmc/host/Makefile14
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c42
-rw-r--r--drivers/mmc/host/sdhci-dove.c42
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c113
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c157
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h17
-rw-r--r--drivers/mmc/host/sdhci-tegra.c116
8 files changed, 313 insertions, 217 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 56dbf3f6ad08..d9ca2623038d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -112,29 +112,19 @@ config MMC_SDHCI_OF_HLWD
112 112
113 If unsure, say N. 113 If unsure, say N.
114 114
115config MMC_SDHCI_PLTFM
116 tristate "SDHCI support on the platform specific bus"
117 depends on MMC_SDHCI
118 help
119 This selects the platform specific bus support for Secure Digital Host
120 Controller Interface.
121
122 If you have a controller with this interface, say Y or M here.
123
124 If unsure, say N.
125
126config MMC_SDHCI_CNS3XXX 115config MMC_SDHCI_CNS3XXX
127 bool "SDHCI support on the Cavium Networks CNS3xxx SoC" 116 tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
128 depends on ARCH_CNS3XXX 117 depends on ARCH_CNS3XXX
129 depends on MMC_SDHCI_PLTFM 118 depends on MMC_SDHCI
130 help 119 help
131 This selects the SDHCI support for CNS3xxx System-on-Chip devices. 120 This selects the SDHCI support for CNS3xxx System-on-Chip devices.
132 121
133 If unsure, say N. 122 If unsure, say N.
134 123
135config MMC_SDHCI_ESDHC_IMX 124config MMC_SDHCI_ESDHC_IMX
136 bool "SDHCI platform support for the Freescale eSDHC i.MX controller" 125 tristate "SDHCI platform support for the Freescale eSDHC i.MX controller"
137 depends on MMC_SDHCI_PLTFM && (ARCH_MX25 || ARCH_MX35 || ARCH_MX5) 126 depends on ARCH_MX25 || ARCH_MX35 || ARCH_MX5
127 depends on MMC_SDHCI
138 select MMC_SDHCI_IO_ACCESSORS 128 select MMC_SDHCI_IO_ACCESSORS
139 help 129 help
140 This selects the Freescale eSDHC controller support on the platform 130 This selects the Freescale eSDHC controller support on the platform
@@ -143,9 +133,9 @@ config MMC_SDHCI_ESDHC_IMX
143 If unsure, say N. 133 If unsure, say N.
144 134
145config MMC_SDHCI_DOVE 135config MMC_SDHCI_DOVE
146 bool "SDHCI support on Marvell's Dove SoC" 136 tristate "SDHCI support on Marvell's Dove SoC"
147 depends on ARCH_DOVE 137 depends on ARCH_DOVE
148 depends on MMC_SDHCI_PLTFM 138 depends on MMC_SDHCI
149 select MMC_SDHCI_IO_ACCESSORS 139 select MMC_SDHCI_IO_ACCESSORS
150 help 140 help
151 This selects the Secure Digital Host Controller Interface in 141 This selects the Secure Digital Host Controller Interface in
@@ -154,8 +144,9 @@ config MMC_SDHCI_DOVE
154 If unsure, say N. 144 If unsure, say N.
155 145
156config MMC_SDHCI_TEGRA 146config MMC_SDHCI_TEGRA
157 bool "SDHCI platform support for the Tegra SD/MMC Controller" 147 tristate "SDHCI platform support for the Tegra SD/MMC Controller"
158 depends on MMC_SDHCI_PLTFM && ARCH_TEGRA 148 depends on ARCH_TEGRA
149 depends on MMC_SDHCI
159 select MMC_SDHCI_IO_ACCESSORS 150 select MMC_SDHCI_IO_ACCESSORS
160 help 151 help
161 This selects the Tegra SD/MMC controller. If you have a Tegra 152 This selects the Tegra SD/MMC controller. If you have a Tegra
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 58a5cf73d6e9..732ec1e2a3d0 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -44,12 +44,14 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
44obj-$(CONFIG_MMC_VUB300) += vub300.o 44obj-$(CONFIG_MMC_VUB300) += vub300.o
45obj-$(CONFIG_MMC_USHC) += ushc.o 45obj-$(CONFIG_MMC_USHC) += ushc.o
46 46
47obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o 47obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
48sdhci-platform-y := sdhci-pltfm.o 48sdhci-cns3xxx-objs := sdhci-pltfm.o
49sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o 49obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
50sdhci-platform-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o 50sdhci-esdhc-imx-objs := sdhci-pltfm.o
51sdhci-platform-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o 51obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
52sdhci-platform-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o 52sdhci-dove-objs := sdhci-pltfm.o
53obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
54sdhci-tegra-objs := sdhci-pltfm.o
53 55
54obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o 56obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
55sdhci-of-y := sdhci-of-core.o 57sdhci-of-y := sdhci-of-core.o
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
index 9ebd1d7759dc..ac4b26f555e6 100644
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -86,7 +86,7 @@ static struct sdhci_ops sdhci_cns3xxx_ops = {
86 .set_clock = sdhci_cns3xxx_set_clock, 86 .set_clock = sdhci_cns3xxx_set_clock,
87}; 87};
88 88
89struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { 89static struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
90 .ops = &sdhci_cns3xxx_ops, 90 .ops = &sdhci_cns3xxx_ops,
91 .quirks = SDHCI_QUIRK_BROKEN_DMA | 91 .quirks = SDHCI_QUIRK_BROKEN_DMA |
92 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 92 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
@@ -95,3 +95,43 @@ struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
95 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 95 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
96 SDHCI_QUIRK_NONSTANDARD_CLOCK, 96 SDHCI_QUIRK_NONSTANDARD_CLOCK,
97}; 97};
98
99static int __devinit sdhci_cns3xxx_probe(struct platform_device *pdev)
100{
101 return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata);
102}
103
104static int __devexit sdhci_cns3xxx_remove(struct platform_device *pdev)
105{
106 return sdhci_pltfm_unregister(pdev);
107}
108
109static struct platform_driver sdhci_cns3xxx_driver = {
110 .driver = {
111 .name = "sdhci-cns3xxx",
112 .owner = THIS_MODULE,
113 },
114 .probe = sdhci_cns3xxx_probe,
115 .remove = __devexit_p(sdhci_cns3xxx_remove),
116#ifdef CONFIG_PM
117 .suspend = sdhci_pltfm_suspend,
118 .resume = sdhci_pltfm_resume,
119#endif
120};
121
122static int __init sdhci_cns3xxx_init(void)
123{
124 return platform_driver_register(&sdhci_cns3xxx_driver);
125}
126module_init(sdhci_cns3xxx_init);
127
128static void __exit sdhci_cns3xxx_exit(void)
129{
130 platform_driver_unregister(&sdhci_cns3xxx_driver);
131}
132module_exit(sdhci_cns3xxx_exit);
133
134MODULE_DESCRIPTION("SDHCI driver for CNS3xxx");
135MODULE_AUTHOR("Scott Shu, "
136 "Anton Vorontsov <avorontsov@mvista.com>");
137MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 2aeef4ffed8c..49aa533f23d4 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -61,10 +61,50 @@ static struct sdhci_ops sdhci_dove_ops = {
61 .read_l = sdhci_dove_readl, 61 .read_l = sdhci_dove_readl,
62}; 62};
63 63
64struct sdhci_pltfm_data sdhci_dove_pdata = { 64static struct sdhci_pltfm_data sdhci_dove_pdata = {
65 .ops = &sdhci_dove_ops, 65 .ops = &sdhci_dove_ops,
66 .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 66 .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
67 SDHCI_QUIRK_NO_BUSY_IRQ | 67 SDHCI_QUIRK_NO_BUSY_IRQ |
68 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 68 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
69 SDHCI_QUIRK_FORCE_DMA, 69 SDHCI_QUIRK_FORCE_DMA,
70}; 70};
71
72static int __devinit sdhci_dove_probe(struct platform_device *pdev)
73{
74 return sdhci_pltfm_register(pdev, &sdhci_dove_pdata);
75}
76
77static int __devexit sdhci_dove_remove(struct platform_device *pdev)
78{
79 return sdhci_pltfm_unregister(pdev);
80}
81
82static struct platform_driver sdhci_dove_driver = {
83 .driver = {
84 .name = "sdhci-dove",
85 .owner = THIS_MODULE,
86 },
87 .probe = sdhci_dove_probe,
88 .remove = __devexit_p(sdhci_dove_remove),
89#ifdef CONFIG_PM
90 .suspend = sdhci_pltfm_suspend,
91 .resume = sdhci_pltfm_resume,
92#endif
93};
94
95static int __init sdhci_dove_init(void)
96{
97 return platform_driver_register(&sdhci_dove_driver);
98}
99module_init(sdhci_dove_init);
100
101static void __exit sdhci_dove_exit(void)
102{
103 platform_driver_unregister(&sdhci_dove_driver);
104}
105module_exit(sdhci_dove_exit);
106
107MODULE_DESCRIPTION("SDHCI driver for Dove");
108MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, "
109 "Mike Rapoport <mike@compulab.co.il>");
110MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index a19967d0bfc4..e27ccbb5285b 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -191,16 +191,6 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
191 return clk_get_rate(pltfm_host->clk) / 256 / 16; 191 return clk_get_rate(pltfm_host->clk) / 256 / 16;
192} 192}
193 193
194static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
195{
196 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
197
198 if (boarddata && gpio_is_valid(boarddata->wp_gpio))
199 return gpio_get_value(boarddata->wp_gpio);
200 else
201 return -ENOSYS;
202}
203
204static struct sdhci_ops sdhci_esdhc_ops = { 194static struct sdhci_ops sdhci_esdhc_ops = {
205 .read_l = esdhc_readl_le, 195 .read_l = esdhc_readl_le,
206 .read_w = esdhc_readw_le, 196 .read_w = esdhc_readw_le,
@@ -212,6 +202,24 @@ static struct sdhci_ops sdhci_esdhc_ops = {
212 .get_min_clock = esdhc_pltfm_get_min_clock, 202 .get_min_clock = esdhc_pltfm_get_min_clock,
213}; 203};
214 204
205static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
206 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
207 | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
208 /* ADMA has issues. Might be fixable */
209 .ops = &sdhci_esdhc_ops,
210};
211
212static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
213{
214 struct esdhc_platform_data *boarddata =
215 host->mmc->parent->platform_data;
216
217 if (boarddata && gpio_is_valid(boarddata->wp_gpio))
218 return gpio_get_value(boarddata->wp_gpio);
219 else
220 return -ENOSYS;
221}
222
215static irqreturn_t cd_irq(int irq, void *data) 223static irqreturn_t cd_irq(int irq, void *data)
216{ 224{
217 struct sdhci_host *sdhost = (struct sdhci_host *)data; 225 struct sdhci_host *sdhost = (struct sdhci_host *)data;
@@ -220,30 +228,35 @@ static irqreturn_t cd_irq(int irq, void *data)
220 return IRQ_HANDLED; 228 return IRQ_HANDLED;
221}; 229};
222 230
223static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) 231static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
224{ 232{
225 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 233 struct sdhci_pltfm_host *pltfm_host;
226 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 234 struct sdhci_host *host;
235 struct esdhc_platform_data *boarddata;
227 struct clk *clk; 236 struct clk *clk;
228 int err; 237 int err;
229 struct pltfm_imx_data *imx_data; 238 struct pltfm_imx_data *imx_data;
230 239
240 host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);
241 if (IS_ERR(host))
242 return PTR_ERR(host);
243
244 pltfm_host = sdhci_priv(host);
245
246 imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
247 if (!imx_data)
248 return -ENOMEM;
249 pltfm_host->priv = imx_data;
250
231 clk = clk_get(mmc_dev(host->mmc), NULL); 251 clk = clk_get(mmc_dev(host->mmc), NULL);
232 if (IS_ERR(clk)) { 252 if (IS_ERR(clk)) {
233 dev_err(mmc_dev(host->mmc), "clk err\n"); 253 dev_err(mmc_dev(host->mmc), "clk err\n");
234 return PTR_ERR(clk); 254 err = PTR_ERR(clk);
255 goto err_clk_get;
235 } 256 }
236 clk_enable(clk); 257 clk_enable(clk);
237 pltfm_host->clk = clk; 258 pltfm_host->clk = clk;
238 259
239 imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
240 if (!imx_data) {
241 clk_disable(pltfm_host->clk);
242 clk_put(pltfm_host->clk);
243 return -ENOMEM;
244 }
245 pltfm_host->priv = imx_data;
246
247 if (!cpu_is_mx25()) 260 if (!cpu_is_mx25())
248 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 261 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
249 262
@@ -257,6 +270,7 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
257 if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51())) 270 if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))
258 imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; 271 imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
259 272
273 boarddata = host->mmc->parent->platform_data;
260 if (boarddata) { 274 if (boarddata) {
261 err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); 275 err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
262 if (err) { 276 if (err) {
@@ -289,6 +303,10 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
289 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 303 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
290 } 304 }
291 305
306 err = sdhci_add_host(host);
307 if (err)
308 goto err_add_host;
309
292 return 0; 310 return 0;
293 311
294 no_card_detect_irq: 312 no_card_detect_irq:
@@ -297,14 +315,23 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
297 boarddata->cd_gpio = err; 315 boarddata->cd_gpio = err;
298 not_supported: 316 not_supported:
299 kfree(imx_data); 317 kfree(imx_data);
300 return 0; 318 err_add_host:
319 clk_disable(pltfm_host->clk);
320 clk_put(pltfm_host->clk);
321 err_clk_get:
322 sdhci_pltfm_free(pdev);
323 return err;
301} 324}
302 325
303static void esdhc_pltfm_exit(struct sdhci_host *host) 326static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
304{ 327{
328 struct sdhci_host *host = platform_get_drvdata(pdev);
305 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 329 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
306 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 330 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
307 struct pltfm_imx_data *imx_data = pltfm_host->priv; 331 struct pltfm_imx_data *imx_data = pltfm_host->priv;
332 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
333
334 sdhci_remove_host(host, dead);
308 335
309 if (boarddata && gpio_is_valid(boarddata->wp_gpio)) 336 if (boarddata && gpio_is_valid(boarddata->wp_gpio))
310 gpio_free(boarddata->wp_gpio); 337 gpio_free(boarddata->wp_gpio);
@@ -319,13 +346,37 @@ static void esdhc_pltfm_exit(struct sdhci_host *host)
319 clk_disable(pltfm_host->clk); 346 clk_disable(pltfm_host->clk);
320 clk_put(pltfm_host->clk); 347 clk_put(pltfm_host->clk);
321 kfree(imx_data); 348 kfree(imx_data);
349
350 sdhci_pltfm_free(pdev);
351
352 return 0;
322} 353}
323 354
324struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 355static struct platform_driver sdhci_esdhc_imx_driver = {
325 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA 356 .driver = {
326 | SDHCI_QUIRK_BROKEN_CARD_DETECTION, 357 .name = "sdhci-esdhc-imx",
327 /* ADMA has issues. Might be fixable */ 358 .owner = THIS_MODULE,
328 .ops = &sdhci_esdhc_ops, 359 },
329 .init = esdhc_pltfm_init, 360 .probe = sdhci_esdhc_imx_probe,
330 .exit = esdhc_pltfm_exit, 361 .remove = __devexit_p(sdhci_esdhc_imx_remove),
362#ifdef CONFIG_PM
363 .suspend = sdhci_pltfm_suspend,
364 .resume = sdhci_pltfm_resume,
365#endif
331}; 366};
367
368static int __init sdhci_esdhc_imx_init(void)
369{
370 return platform_driver_register(&sdhci_esdhc_imx_driver);
371}
372module_init(sdhci_esdhc_imx_init);
373
374static void __exit sdhci_esdhc_imx_exit(void)
375{
376 platform_driver_unregister(&sdhci_esdhc_imx_driver);
377}
378module_exit(sdhci_esdhc_imx_exit);
379
380MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
381MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
382MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index dbab0407f4b6..8ccf25666201 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -22,48 +22,22 @@
22 * Inspired by sdhci-pci.c, by Pierre Ossman 22 * Inspired by sdhci-pci.c, by Pierre Ossman
23 */ 23 */
24 24
25#include <linux/delay.h> 25#include <linux/err.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 26
35#include "sdhci.h" 27#include "sdhci.h"
36#include "sdhci-pltfm.h" 28#include "sdhci-pltfm.h"
37 29
38/*****************************************************************************\
39 * *
40 * SDHCI core callbacks *
41 * *
42\*****************************************************************************/
43
44static struct sdhci_ops sdhci_pltfm_ops = { 30static struct sdhci_ops sdhci_pltfm_ops = {
45}; 31};
46 32
47/*****************************************************************************\ 33struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
48 * * 34 struct sdhci_pltfm_data *pdata)
49 * Device probing/removal *
50 * *
51\*****************************************************************************/
52
53static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
54{ 35{
55 const struct platform_device_id *platid = platform_get_device_id(pdev);
56 struct sdhci_pltfm_data *pdata;
57 struct sdhci_host *host; 36 struct sdhci_host *host;
58 struct sdhci_pltfm_host *pltfm_host; 37 struct sdhci_pltfm_host *pltfm_host;
59 struct resource *iomem; 38 struct resource *iomem;
60 int ret; 39 int ret;
61 40
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); 41 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
68 if (!iomem) { 42 if (!iomem) {
69 ret = -ENOMEM; 43 ret = -ENOMEM;
@@ -71,8 +45,7 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
71 } 45 }
72 46
73 if (resource_size(iomem) < 0x100) 47 if (resource_size(iomem) < 0x100)
74 dev_err(&pdev->dev, "Invalid iomem size. You may " 48 dev_err(&pdev->dev, "Invalid iomem size!\n");
75 "experience problems.\n");
76 49
77 /* Some PCI-based MFD need the parent here */ 50 /* Some PCI-based MFD need the parent here */
78 if (pdev->dev.parent != &platform_bus) 51 if (pdev->dev.parent != &platform_bus)
@@ -87,7 +60,7 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
87 60
88 pltfm_host = sdhci_priv(host); 61 pltfm_host = sdhci_priv(host);
89 62
90 host->hw_name = "platform"; 63 host->hw_name = dev_name(&pdev->dev);
91 if (pdata && pdata->ops) 64 if (pdata && pdata->ops)
92 host->ops = pdata->ops; 65 host->ops = pdata->ops;
93 else 66 else
@@ -110,126 +83,70 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
110 goto err_remap; 83 goto err_remap;
111 } 84 }
112 85
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); 86 platform_set_drvdata(pdev, host);
124 87
125 return 0; 88 return host;
126 89
127err_add_host:
128 if (pdata && pdata->exit)
129 pdata->exit(host);
130err_plat_init:
131 iounmap(host->ioaddr);
132err_remap: 90err_remap:
133 release_mem_region(iomem->start, resource_size(iomem)); 91 release_mem_region(iomem->start, resource_size(iomem));
134err_request: 92err_request:
135 sdhci_free_host(host); 93 sdhci_free_host(host);
136err: 94err:
137 printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret); 95 dev_err(&pdev->dev, "%s failed %d\n", __func__, ret);
138 return ret; 96 return ERR_PTR(ret);
139} 97}
140 98
141static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) 99void sdhci_pltfm_free(struct platform_device *pdev)
142{ 100{
143 struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
144 struct sdhci_host *host = platform_get_drvdata(pdev); 101 struct sdhci_host *host = platform_get_drvdata(pdev);
145 struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 102 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 103
154 sdhci_remove_host(host, dead);
155 if (pdata && pdata->exit)
156 pdata->exit(host);
157 iounmap(host->ioaddr); 104 iounmap(host->ioaddr);
158 release_mem_region(iomem->start, resource_size(iomem)); 105 release_mem_region(iomem->start, resource_size(iomem));
159 sdhci_free_host(host); 106 sdhci_free_host(host);
160 platform_set_drvdata(pdev, NULL); 107 platform_set_drvdata(pdev, NULL);
108}
161 109
162 return 0; 110int sdhci_pltfm_register(struct platform_device *pdev,
111 struct sdhci_pltfm_data *pdata)
112{
113 struct sdhci_host *host;
114 int ret = 0;
115
116 host = sdhci_pltfm_init(pdev, pdata);
117 if (IS_ERR(host))
118 return PTR_ERR(host);
119
120 ret = sdhci_add_host(host);
121 if (ret)
122 sdhci_pltfm_free(pdev);
123
124 return ret;
163} 125}
164 126
165static const struct platform_device_id sdhci_pltfm_ids[] = { 127int sdhci_pltfm_unregister(struct platform_device *pdev)
166 { "sdhci", }, 128{
167#ifdef CONFIG_MMC_SDHCI_CNS3XXX 129 struct sdhci_host *host = platform_get_drvdata(pdev);
168 { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata }, 130 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
169#endif 131
170#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX 132 sdhci_remove_host(host, dead);
171 { "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata }, 133 sdhci_pltfm_free(pdev);
172#endif 134
173#ifdef CONFIG_MMC_SDHCI_DOVE 135 return 0;
174 { "sdhci-dove", (kernel_ulong_t)&sdhci_dove_pdata }, 136}
175#endif
176#ifdef CONFIG_MMC_SDHCI_TEGRA
177 { "sdhci-tegra", (kernel_ulong_t)&sdhci_tegra_pdata },
178#endif
179 { },
180};
181MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
182 137
183#ifdef CONFIG_PM 138#ifdef CONFIG_PM
184static int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state) 139int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
185{ 140{
186 struct sdhci_host *host = platform_get_drvdata(dev); 141 struct sdhci_host *host = platform_get_drvdata(dev);
187 142
188 return sdhci_suspend_host(host, state); 143 return sdhci_suspend_host(host, state);
189} 144}
190 145
191static int sdhci_pltfm_resume(struct platform_device *dev) 146int sdhci_pltfm_resume(struct platform_device *dev)
192{ 147{
193 struct sdhci_host *host = platform_get_drvdata(dev); 148 struct sdhci_host *host = platform_get_drvdata(dev);
194 149
195 return sdhci_resume_host(host); 150 return sdhci_resume_host(host);
196} 151}
197#else
198#define sdhci_pltfm_suspend NULL
199#define sdhci_pltfm_resume NULL
200#endif /* CONFIG_PM */ 152#endif /* CONFIG_PM */
201
202static 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
220static int __init sdhci_drv_init(void)
221{
222 return platform_driver_register(&sdhci_pltfm_driver);
223}
224
225static void __exit sdhci_drv_exit(void)
226{
227 platform_driver_unregister(&sdhci_pltfm_driver);
228}
229
230module_init(sdhci_drv_init);
231module_exit(sdhci_drv_exit);
232
233MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
234MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
235MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index 2b37016ad0ac..ff4b7eb326fb 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -13,6 +13,7 @@
13 13
14#include <linux/clk.h> 14#include <linux/clk.h>
15#include <linux/types.h> 15#include <linux/types.h>
16#include <linux/platform_device.h>
16#include <linux/mmc/sdhci-pltfm.h> 17#include <linux/mmc/sdhci-pltfm.h>
17 18
18struct sdhci_pltfm_host { 19struct sdhci_pltfm_host {
@@ -20,9 +21,17 @@ struct sdhci_pltfm_host {
20 void *priv; /* to handle quirks across io-accessor calls */ 21 void *priv; /* to handle quirks across io-accessor calls */
21}; 22};
22 23
23extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; 24extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
24extern struct sdhci_pltfm_data sdhci_esdhc_imx_pdata; 25 struct sdhci_pltfm_data *pdata);
25extern struct sdhci_pltfm_data sdhci_dove_pdata; 26extern void sdhci_pltfm_free(struct platform_device *pdev);
26extern struct sdhci_pltfm_data sdhci_tegra_pdata; 27
28extern int sdhci_pltfm_register(struct platform_device *pdev,
29 struct sdhci_pltfm_data *pdata);
30extern int sdhci_pltfm_unregister(struct platform_device *pdev);
31
32#ifdef CONFIG_PM
33extern int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state);
34extern int sdhci_pltfm_resume(struct platform_device *dev);
35#endif
27 36
28#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ 37#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 343c97edba32..1f66aca5f506 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -116,20 +116,42 @@ static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
116 return 0; 116 return 0;
117} 117}
118 118
119static struct sdhci_ops tegra_sdhci_ops = {
120 .get_ro = tegra_sdhci_get_ro,
121 .read_l = tegra_sdhci_readl,
122 .read_w = tegra_sdhci_readw,
123 .write_l = tegra_sdhci_writel,
124 .platform_8bit_width = tegra_sdhci_8bit,
125};
126
127static struct sdhci_pltfm_data sdhci_tegra_pdata = {
128 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
129 SDHCI_QUIRK_SINGLE_POWER_WRITE |
130 SDHCI_QUIRK_NO_HISPD_BIT |
131 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
132 .ops = &tegra_sdhci_ops,
133};
119 134
120static int tegra_sdhci_pltfm_init(struct sdhci_host *host, 135static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
121 struct sdhci_pltfm_data *pdata)
122{ 136{
123 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 137 struct sdhci_pltfm_host *pltfm_host;
124 struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
125 struct tegra_sdhci_platform_data *plat; 138 struct tegra_sdhci_platform_data *plat;
139 struct sdhci_host *host;
126 struct clk *clk; 140 struct clk *clk;
127 int rc; 141 int rc;
128 142
143 host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata);
144 if (IS_ERR(host))
145 return PTR_ERR(host);
146
147 pltfm_host = sdhci_priv(host);
148
129 plat = pdev->dev.platform_data; 149 plat = pdev->dev.platform_data;
150
130 if (plat == NULL) { 151 if (plat == NULL) {
131 dev_err(mmc_dev(host->mmc), "missing platform data\n"); 152 dev_err(mmc_dev(host->mmc), "missing platform data\n");
132 return -ENXIO; 153 rc = -ENXIO;
154 goto err_no_plat;
133 } 155 }
134 156
135 if (gpio_is_valid(plat->power_gpio)) { 157 if (gpio_is_valid(plat->power_gpio)) {
@@ -137,7 +159,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
137 if (rc) { 159 if (rc) {
138 dev_err(mmc_dev(host->mmc), 160 dev_err(mmc_dev(host->mmc),
139 "failed to allocate power gpio\n"); 161 "failed to allocate power gpio\n");
140 goto out; 162 goto err_power_req;
141 } 163 }
142 tegra_gpio_enable(plat->power_gpio); 164 tegra_gpio_enable(plat->power_gpio);
143 gpio_direction_output(plat->power_gpio, 1); 165 gpio_direction_output(plat->power_gpio, 1);
@@ -148,7 +170,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
148 if (rc) { 170 if (rc) {
149 dev_err(mmc_dev(host->mmc), 171 dev_err(mmc_dev(host->mmc),
150 "failed to allocate cd gpio\n"); 172 "failed to allocate cd gpio\n");
151 goto out_power; 173 goto err_cd_req;
152 } 174 }
153 tegra_gpio_enable(plat->cd_gpio); 175 tegra_gpio_enable(plat->cd_gpio);
154 gpio_direction_input(plat->cd_gpio); 176 gpio_direction_input(plat->cd_gpio);
@@ -159,7 +181,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
159 181
160 if (rc) { 182 if (rc) {
161 dev_err(mmc_dev(host->mmc), "request irq error\n"); 183 dev_err(mmc_dev(host->mmc), "request irq error\n");
162 goto out_cd; 184 goto err_cd_irq_req;
163 } 185 }
164 186
165 } 187 }
@@ -169,7 +191,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
169 if (rc) { 191 if (rc) {
170 dev_err(mmc_dev(host->mmc), 192 dev_err(mmc_dev(host->mmc),
171 "failed to allocate wp gpio\n"); 193 "failed to allocate wp gpio\n");
172 goto out_irq; 194 goto err_wp_req;
173 } 195 }
174 tegra_gpio_enable(plat->wp_gpio); 196 tegra_gpio_enable(plat->wp_gpio);
175 gpio_direction_input(plat->wp_gpio); 197 gpio_direction_input(plat->wp_gpio);
@@ -179,7 +201,7 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
179 if (IS_ERR(clk)) { 201 if (IS_ERR(clk)) {
180 dev_err(mmc_dev(host->mmc), "clk err\n"); 202 dev_err(mmc_dev(host->mmc), "clk err\n");
181 rc = PTR_ERR(clk); 203 rc = PTR_ERR(clk);
182 goto out_wp; 204 goto err_clk_get;
183 } 205 }
184 clk_enable(clk); 206 clk_enable(clk);
185 pltfm_host->clk = clk; 207 pltfm_host->clk = clk;
@@ -189,38 +211,47 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
189 if (plat->is_8bit) 211 if (plat->is_8bit)
190 host->mmc->caps |= MMC_CAP_8_BIT_DATA; 212 host->mmc->caps |= MMC_CAP_8_BIT_DATA;
191 213
214 rc = sdhci_add_host(host);
215 if (rc)
216 goto err_add_host;
217
192 return 0; 218 return 0;
193 219
194out_wp: 220err_add_host:
221 clk_disable(pltfm_host->clk);
222 clk_put(pltfm_host->clk);
223err_clk_get:
195 if (gpio_is_valid(plat->wp_gpio)) { 224 if (gpio_is_valid(plat->wp_gpio)) {
196 tegra_gpio_disable(plat->wp_gpio); 225 tegra_gpio_disable(plat->wp_gpio);
197 gpio_free(plat->wp_gpio); 226 gpio_free(plat->wp_gpio);
198 } 227 }
199 228err_wp_req:
200out_irq:
201 if (gpio_is_valid(plat->cd_gpio)) 229 if (gpio_is_valid(plat->cd_gpio))
202 free_irq(gpio_to_irq(plat->cd_gpio), host); 230 free_irq(gpio_to_irq(plat->cd_gpio), host);
203out_cd: 231err_cd_irq_req:
204 if (gpio_is_valid(plat->cd_gpio)) { 232 if (gpio_is_valid(plat->cd_gpio)) {
205 tegra_gpio_disable(plat->cd_gpio); 233 tegra_gpio_disable(plat->cd_gpio);
206 gpio_free(plat->cd_gpio); 234 gpio_free(plat->cd_gpio);
207 } 235 }
208 236err_cd_req:
209out_power:
210 if (gpio_is_valid(plat->power_gpio)) { 237 if (gpio_is_valid(plat->power_gpio)) {
211 tegra_gpio_disable(plat->power_gpio); 238 tegra_gpio_disable(plat->power_gpio);
212 gpio_free(plat->power_gpio); 239 gpio_free(plat->power_gpio);
213 } 240 }
214 241err_power_req:
215out: 242err_no_plat:
243 sdhci_pltfm_free(pdev);
216 return rc; 244 return rc;
217} 245}
218 246
219static void tegra_sdhci_pltfm_exit(struct sdhci_host *host) 247static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
220{ 248{
249 struct sdhci_host *host = platform_get_drvdata(pdev);
221 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 250 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
222 struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
223 struct tegra_sdhci_platform_data *plat; 251 struct tegra_sdhci_platform_data *plat;
252 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
253
254 sdhci_remove_host(host, dead);
224 255
225 plat = pdev->dev.platform_data; 256 plat = pdev->dev.platform_data;
226 257
@@ -242,22 +273,37 @@ static void tegra_sdhci_pltfm_exit(struct sdhci_host *host)
242 273
243 clk_disable(pltfm_host->clk); 274 clk_disable(pltfm_host->clk);
244 clk_put(pltfm_host->clk); 275 clk_put(pltfm_host->clk);
276
277 sdhci_pltfm_free(pdev);
278
279 return 0;
245} 280}
246 281
247static struct sdhci_ops tegra_sdhci_ops = { 282static struct platform_driver sdhci_tegra_driver = {
248 .get_ro = tegra_sdhci_get_ro, 283 .driver = {
249 .read_l = tegra_sdhci_readl, 284 .name = "sdhci-tegra",
250 .read_w = tegra_sdhci_readw, 285 .owner = THIS_MODULE,
251 .write_l = tegra_sdhci_writel, 286 },
252 .platform_8bit_width = tegra_sdhci_8bit, 287 .probe = sdhci_tegra_probe,
288 .remove = __devexit_p(sdhci_tegra_remove),
289#ifdef CONFIG_PM
290 .suspend = sdhci_pltfm_suspend,
291 .resume = sdhci_pltfm_resume,
292#endif
253}; 293};
254 294
255struct sdhci_pltfm_data sdhci_tegra_pdata = { 295static int __init sdhci_tegra_init(void)
256 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 296{
257 SDHCI_QUIRK_SINGLE_POWER_WRITE | 297 return platform_driver_register(&sdhci_tegra_driver);
258 SDHCI_QUIRK_NO_HISPD_BIT | 298}
259 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 299module_init(sdhci_tegra_init);
260 .ops = &tegra_sdhci_ops, 300
261 .init = tegra_sdhci_pltfm_init, 301static void __exit sdhci_tegra_exit(void)
262 .exit = tegra_sdhci_pltfm_exit, 302{
263}; 303 platform_driver_unregister(&sdhci_tegra_driver);
304}
305module_exit(sdhci_tegra_exit);
306
307MODULE_DESCRIPTION("SDHCI driver for Tegra");
308MODULE_AUTHOR(" Google, Inc.");
309MODULE_LICENSE("GPL v2");