xref: /openbmc/linux/drivers/mmc/host/sdhci-pltfm.c (revision baa7eb025ab14f3cba2e35c0a8648f9c9f01d24f)
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 	{ },
174 };
175 MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
176 
177 #ifdef CONFIG_PM
178 static int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
179 {
180 	struct sdhci_host *host = platform_get_drvdata(dev);
181 
182 	return sdhci_suspend_host(host, state);
183 }
184 
185 static int sdhci_pltfm_resume(struct platform_device *dev)
186 {
187 	struct sdhci_host *host = platform_get_drvdata(dev);
188 
189 	return sdhci_resume_host(host);
190 }
191 #else
192 #define sdhci_pltfm_suspend	NULL
193 #define sdhci_pltfm_resume	NULL
194 #endif	/* CONFIG_PM */
195 
196 static struct platform_driver sdhci_pltfm_driver = {
197 	.driver = {
198 		.name	= "sdhci",
199 		.owner	= THIS_MODULE,
200 	},
201 	.probe		= sdhci_pltfm_probe,
202 	.remove		= __devexit_p(sdhci_pltfm_remove),
203 	.id_table	= sdhci_pltfm_ids,
204 	.suspend	= sdhci_pltfm_suspend,
205 	.resume		= sdhci_pltfm_resume,
206 };
207 
208 /*****************************************************************************\
209  *                                                                           *
210  * Driver init/exit                                                          *
211  *                                                                           *
212 \*****************************************************************************/
213 
214 static int __init sdhci_drv_init(void)
215 {
216 	return platform_driver_register(&sdhci_pltfm_driver);
217 }
218 
219 static void __exit sdhci_drv_exit(void)
220 {
221 	platform_driver_unregister(&sdhci_pltfm_driver);
222 }
223 
224 module_init(sdhci_drv_init);
225 module_exit(sdhci_drv_exit);
226 
227 MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
228 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
229 MODULE_LICENSE("GPL v2");
230