blob: 8ccf2566620128df94e3cbf8af77c057cdb8af2a [file] [log] [blame]
Richard Röjforsa3456a22009-06-04 13:57:29 +02001/*
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
Shawn Guo85d65092011-05-27 23:48:12 +080025#include <linux/err.h>
Richard Röjforsa3456a22009-06-04 13:57:29 +020026
27#include "sdhci.h"
Anton Vorontsov515033f2010-08-10 18:01:47 -070028#include "sdhci-pltfm.h"
Richard Röjforsa3456a22009-06-04 13:57:29 +020029
Richard Röjforsa3456a22009-06-04 13:57:29 +020030static struct sdhci_ops sdhci_pltfm_ops = {
31};
32
Shawn Guo85d65092011-05-27 23:48:12 +080033struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
34 struct sdhci_pltfm_data *pdata)
Richard Röjforsa3456a22009-06-04 13:57:29 +020035{
36 struct sdhci_host *host;
Wolfram Sang4b711cb2010-10-15 12:20:59 +020037 struct sdhci_pltfm_host *pltfm_host;
Richard Röjforsa3456a22009-06-04 13:57:29 +020038 struct resource *iomem;
39 int ret;
40
Richard Röjforsa3456a22009-06-04 13:57:29 +020041 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
42 if (!iomem) {
43 ret = -ENOMEM;
44 goto err;
45 }
46
Anton Vorontsove632c452010-05-26 14:41:56 -070047 if (resource_size(iomem) < 0x100)
Shawn Guo85d65092011-05-27 23:48:12 +080048 dev_err(&pdev->dev, "Invalid iomem size!\n");
Richard Röjforsa3456a22009-06-04 13:57:29 +020049
Wolfram Sang4b711cb2010-10-15 12:20:59 +020050 /* Some PCI-based MFD need the parent here */
51 if (pdev->dev.parent != &platform_bus)
52 host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
Richard Röjforsa3456a22009-06-04 13:57:29 +020053 else
Wolfram Sang4b711cb2010-10-15 12:20:59 +020054 host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
Richard Röjforsa3456a22009-06-04 13:57:29 +020055
56 if (IS_ERR(host)) {
57 ret = PTR_ERR(host);
58 goto err;
59 }
60
Wolfram Sang4b711cb2010-10-15 12:20:59 +020061 pltfm_host = sdhci_priv(host);
62
Shawn Guo85d65092011-05-27 23:48:12 +080063 host->hw_name = dev_name(&pdev->dev);
Anton Vorontsova7626b72010-05-26 14:41:55 -070064 if (pdata && pdata->ops)
65 host->ops = pdata->ops;
66 else
67 host->ops = &sdhci_pltfm_ops;
68 if (pdata)
69 host->quirks = pdata->quirks;
Richard Röjforsa3456a22009-06-04 13:57:29 +020070 host->irq = platform_get_irq(pdev, 0);
71
72 if (!request_mem_region(iomem->start, resource_size(iomem),
73 mmc_hostname(host->mmc))) {
74 dev_err(&pdev->dev, "cannot request region\n");
75 ret = -EBUSY;
76 goto err_request;
77 }
78
79 host->ioaddr = ioremap(iomem->start, resource_size(iomem));
80 if (!host->ioaddr) {
81 dev_err(&pdev->dev, "failed to remap registers\n");
82 ret = -ENOMEM;
83 goto err_remap;
84 }
85
Richard Röjforsa3456a22009-06-04 13:57:29 +020086 platform_set_drvdata(pdev, host);
87
Shawn Guo85d65092011-05-27 23:48:12 +080088 return host;
Richard Röjforsa3456a22009-06-04 13:57:29 +020089
Richard Röjforsa3456a22009-06-04 13:57:29 +020090err_remap:
91 release_mem_region(iomem->start, resource_size(iomem));
92err_request:
93 sdhci_free_host(host);
94err:
Shawn Guo85d65092011-05-27 23:48:12 +080095 dev_err(&pdev->dev, "%s failed %d\n", __func__, ret);
96 return ERR_PTR(ret);
Richard Röjforsa3456a22009-06-04 13:57:29 +020097}
98
Shawn Guo85d65092011-05-27 23:48:12 +080099void sdhci_pltfm_free(struct platform_device *pdev)
Richard Röjforsa3456a22009-06-04 13:57:29 +0200100{
101 struct sdhci_host *host = platform_get_drvdata(pdev);
102 struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Röjforsa3456a22009-06-04 13:57:29 +0200103
Richard Röjforsa3456a22009-06-04 13:57:29 +0200104 iounmap(host->ioaddr);
105 release_mem_region(iomem->start, resource_size(iomem));
106 sdhci_free_host(host);
107 platform_set_drvdata(pdev, NULL);
Shawn Guo85d65092011-05-27 23:48:12 +0800108}
109
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;
125}
126
127int sdhci_pltfm_unregister(struct platform_device *pdev)
128{
129 struct sdhci_host *host = platform_get_drvdata(pdev);
130 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
131
132 sdhci_remove_host(host, dead);
133 sdhci_pltfm_free(pdev);
Richard Röjforsa3456a22009-06-04 13:57:29 +0200134
135 return 0;
136}
137
Giuseppe Cavallarobe8ae092010-09-28 10:41:27 +0200138#ifdef CONFIG_PM
Shawn Guo85d65092011-05-27 23:48:12 +0800139int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
Giuseppe Cavallarobe8ae092010-09-28 10:41:27 +0200140{
141 struct sdhci_host *host = platform_get_drvdata(dev);
142
143 return sdhci_suspend_host(host, state);
144}
145
Shawn Guo85d65092011-05-27 23:48:12 +0800146int sdhci_pltfm_resume(struct platform_device *dev)
Giuseppe Cavallarobe8ae092010-09-28 10:41:27 +0200147{
148 struct sdhci_host *host = platform_get_drvdata(dev);
149
150 return sdhci_resume_host(host);
151}
Giuseppe Cavallarobe8ae092010-09-28 10:41:27 +0200152#endif /* CONFIG_PM */