PageRenderTime 50ms CodeModel.GetById 38ms app.highlight 9ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/mmc/host/sdhci-pltfm.c

https://bitbucket.org/ndreys/linux-sunxi
C | 235 lines | 159 code | 38 blank | 38 comment | 22 complexity | 489d17df1310dbee6ac3cadb0d6e262a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  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
 44static struct sdhci_ops sdhci_pltfm_ops = {
 45};
 46
 47/*****************************************************************************\
 48 *                                                                           *
 49 * Device probing/removal                                                    *
 50 *                                                                           *
 51\*****************************************************************************/
 52
 53static 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
127err_add_host:
128	if (pdata && pdata->exit)
129		pdata->exit(host);
130err_plat_init:
131	iounmap(host->ioaddr);
132err_remap:
133	release_mem_region(iomem->start, resource_size(iomem));
134err_request:
135	sdhci_free_host(host);
136err:
137	printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret);
138	return ret;
139}
140
141static 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
165static 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};
181MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
182
183#ifdef CONFIG_PM
184static 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
191static 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
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");