mmc: sdhci-bcm-kona: fix build errors when built-in
[deliverable/linux.git] / drivers / mmc / host / sdhci-spear.c
CommitLineData
c63b3cba
VK
1/*
2 * drivers/mmc/host/sdhci-spear.c
3 *
4 * Support of SDHCI platform devices for spear soc family
5 *
6 * Copyright (C) 2010 ST Microelectronics
10d8935f 7 * Viresh Kumar <viresh.linux@gmail.com>
c63b3cba
VK
8 *
9 * Inspired by sdhci-pltfm.c
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#include <linux/clk.h>
17#include <linux/delay.h>
18#include <linux/gpio.h>
19#include <linux/highmem.h>
88b47679 20#include <linux/module.h>
c63b3cba
VK
21#include <linux/interrupt.h>
22#include <linux/irq.h>
067bf748
VK
23#include <linux/of.h>
24#include <linux/of_gpio.h>
c63b3cba 25#include <linux/platform_device.h>
b70a7fab 26#include <linux/pm.h>
c63b3cba
VK
27#include <linux/slab.h>
28#include <linux/mmc/host.h>
29#include <linux/mmc/sdhci-spear.h>
30#include <linux/io.h>
31#include "sdhci.h"
32
33struct spear_sdhci {
34 struct clk *clk;
35 struct sdhci_plat_data *data;
36};
37
38/* sdhci ops */
c915568d 39static const struct sdhci_ops sdhci_pltfm_ops = {
c63b3cba
VK
40 /* Nothing to do for now. */
41};
42
43/* gpio card detection interrupt handler */
44static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
45{
46 struct platform_device *pdev = dev_id;
47 struct sdhci_host *host = platform_get_drvdata(pdev);
48 struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
49 unsigned long gpio_irq_type;
50 int val;
51
52 val = gpio_get_value(sdhci->data->card_int_gpio);
53
54 /* val == 1 -> card removed, val == 0 -> card inserted */
55 /* if card removed - set irq for low level, else vice versa */
56 gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
dced35ae 57 irq_set_irq_type(irq, gpio_irq_type);
c63b3cba
VK
58
59 if (sdhci->data->card_power_gpio >= 0) {
60 if (!sdhci->data->power_always_enb) {
61 /* if card inserted, give power, otherwise remove it */
62 val = sdhci->data->power_active_high ? !val : val ;
63 gpio_set_value(sdhci->data->card_power_gpio, val);
64 }
65 }
66
67 /* inform sdhci driver about card insertion/removal */
68 tasklet_schedule(&host->card_tasklet);
69
70 return IRQ_HANDLED;
71}
72
067bf748 73#ifdef CONFIG_OF
c3be1efd 74static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
067bf748
VK
75{
76 struct device_node *np = pdev->dev.of_node;
77 struct sdhci_plat_data *pdata = NULL;
78 int cd_gpio;
79
80 cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
81 if (!gpio_is_valid(cd_gpio))
82 cd_gpio = -1;
83
84 /* If pdata is required */
85 if (cd_gpio != -1) {
86 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
eab36b24 87 if (!pdata)
067bf748 88 dev_err(&pdev->dev, "DT: kzalloc failed\n");
eab36b24
SK
89 else
90 pdata->card_int_gpio = cd_gpio;
067bf748
VK
91 }
92
067bf748
VK
93 return pdata;
94}
95#else
c3be1efd 96static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
067bf748
VK
97{
98 return ERR_PTR(-ENOSYS);
99}
100#endif
101
c3be1efd 102static int sdhci_probe(struct platform_device *pdev)
c63b3cba 103{
067bf748 104 struct device_node *np = pdev->dev.of_node;
c63b3cba
VK
105 struct sdhci_host *host;
106 struct resource *iomem;
107 struct spear_sdhci *sdhci;
108 int ret;
109
c63b3cba
VK
110 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
111 if (!iomem) {
112 ret = -ENOMEM;
113 dev_dbg(&pdev->dev, "memory resource not defined\n");
114 goto err;
115 }
116
6ebaf8f2
VK
117 if (!devm_request_mem_region(&pdev->dev, iomem->start,
118 resource_size(iomem), "spear-sdhci")) {
c63b3cba
VK
119 ret = -EBUSY;
120 dev_dbg(&pdev->dev, "cannot request region\n");
121 goto err;
122 }
123
6ebaf8f2 124 sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
c63b3cba
VK
125 if (!sdhci) {
126 ret = -ENOMEM;
127 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
6ebaf8f2 128 goto err;
c63b3cba
VK
129 }
130
131 /* clk enable */
132 sdhci->clk = clk_get(&pdev->dev, NULL);
133 if (IS_ERR(sdhci->clk)) {
134 ret = PTR_ERR(sdhci->clk);
135 dev_dbg(&pdev->dev, "Error getting clock\n");
6ebaf8f2 136 goto err;
c63b3cba
VK
137 }
138
da764f97 139 ret = clk_prepare_enable(sdhci->clk);
c63b3cba
VK
140 if (ret) {
141 dev_dbg(&pdev->dev, "Error enabling clock\n");
6ebaf8f2 142 goto put_clk;
c63b3cba
VK
143 }
144
257f9df1
VKS
145 ret = clk_set_rate(sdhci->clk, 50000000);
146 if (ret)
147 dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
148 clk_get_rate(sdhci->clk));
149
067bf748
VK
150 if (np) {
151 sdhci->data = sdhci_probe_config_dt(pdev);
152 if (IS_ERR(sdhci->data)) {
153 dev_err(&pdev->dev, "DT: Failed to get pdata\n");
154 return -ENODEV;
155 }
156 } else {
157 sdhci->data = dev_get_platdata(&pdev->dev);
158 }
159
c63b3cba
VK
160 pdev->dev.platform_data = sdhci;
161
162 if (pdev->dev.parent)
163 host = sdhci_alloc_host(pdev->dev.parent, 0);
164 else
165 host = sdhci_alloc_host(&pdev->dev, 0);
166
167 if (IS_ERR(host)) {
168 ret = PTR_ERR(host);
169 dev_dbg(&pdev->dev, "error allocating host\n");
6ebaf8f2 170 goto disable_clk;
c63b3cba
VK
171 }
172
173 host->hw_name = "sdhci";
174 host->ops = &sdhci_pltfm_ops;
175 host->irq = platform_get_irq(pdev, 0);
176 host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
177
6ebaf8f2
VK
178 host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
179 resource_size(iomem));
c63b3cba
VK
180 if (!host->ioaddr) {
181 ret = -ENOMEM;
182 dev_dbg(&pdev->dev, "failed to remap registers\n");
6ebaf8f2 183 goto free_host;
c63b3cba
VK
184 }
185
186 ret = sdhci_add_host(host);
187 if (ret) {
188 dev_dbg(&pdev->dev, "error adding host\n");
6ebaf8f2 189 goto free_host;
c63b3cba
VK
190 }
191
192 platform_set_drvdata(pdev, host);
193
194 /*
195 * It is optional to use GPIOs for sdhci Power control & sdhci card
196 * interrupt detection. If sdhci->data is NULL, then use original sdhci
197 * lines otherwise GPIO lines.
198 * If GPIO is selected for power control, then power should be disabled
199 * after card removal and should be enabled when card insertion
200 * interrupt occurs
201 */
202 if (!sdhci->data)
203 return 0;
204
205 if (sdhci->data->card_power_gpio >= 0) {
206 int val = 0;
207
6ebaf8f2
VK
208 ret = devm_gpio_request(&pdev->dev,
209 sdhci->data->card_power_gpio, "sdhci");
c63b3cba
VK
210 if (ret < 0) {
211 dev_dbg(&pdev->dev, "gpio request fail: %d\n",
212 sdhci->data->card_power_gpio);
6ebaf8f2 213 goto set_drvdata;
c63b3cba
VK
214 }
215
216 if (sdhci->data->power_always_enb)
217 val = sdhci->data->power_active_high;
218 else
219 val = !sdhci->data->power_active_high;
220
221 ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
222 if (ret) {
223 dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
224 sdhci->data->card_power_gpio);
6ebaf8f2 225 goto set_drvdata;
c63b3cba 226 }
c63b3cba
VK
227 }
228
229 if (sdhci->data->card_int_gpio >= 0) {
6ebaf8f2
VK
230 ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
231 "sdhci");
c63b3cba
VK
232 if (ret < 0) {
233 dev_dbg(&pdev->dev, "gpio request fail: %d\n",
234 sdhci->data->card_int_gpio);
6ebaf8f2 235 goto set_drvdata;
c63b3cba
VK
236 }
237
238 ret = gpio_direction_input(sdhci->data->card_int_gpio);
239 if (ret) {
240 dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
241 sdhci->data->card_int_gpio);
6ebaf8f2 242 goto set_drvdata;
c63b3cba 243 }
6ebaf8f2
VK
244 ret = devm_request_irq(&pdev->dev,
245 gpio_to_irq(sdhci->data->card_int_gpio),
c63b3cba
VK
246 sdhci_gpio_irq, IRQF_TRIGGER_LOW,
247 mmc_hostname(host->mmc), pdev);
248 if (ret) {
249 dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
250 sdhci->data->card_int_gpio);
6ebaf8f2 251 goto set_drvdata;
c63b3cba
VK
252 }
253
254 }
255
256 return 0;
257
6ebaf8f2 258set_drvdata:
c63b3cba 259 sdhci_remove_host(host, 1);
6ebaf8f2 260free_host:
c63b3cba 261 sdhci_free_host(host);
6ebaf8f2 262disable_clk:
da764f97 263 clk_disable_unprepare(sdhci->clk);
6ebaf8f2 264put_clk:
c63b3cba 265 clk_put(sdhci->clk);
c63b3cba
VK
266err:
267 dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
268 return ret;
269}
270
6e0ee714 271static int sdhci_remove(struct platform_device *pdev)
c63b3cba
VK
272{
273 struct sdhci_host *host = platform_get_drvdata(pdev);
c63b3cba 274 struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
6ebaf8f2 275 int dead = 0;
c63b3cba
VK
276 u32 scratch;
277
c63b3cba
VK
278 scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
279 if (scratch == (u32)-1)
280 dead = 1;
281
282 sdhci_remove_host(host, dead);
c63b3cba 283 sdhci_free_host(host);
da764f97 284 clk_disable_unprepare(sdhci->clk);
c63b3cba 285 clk_put(sdhci->clk);
c63b3cba
VK
286
287 return 0;
288}
289
b0dd099c 290#ifdef CONFIG_PM_SLEEP
b70a7fab
VK
291static int sdhci_suspend(struct device *dev)
292{
293 struct sdhci_host *host = dev_get_drvdata(dev);
294 struct spear_sdhci *sdhci = dev_get_platdata(dev);
b70a7fab
VK
295 int ret;
296
984589e5 297 ret = sdhci_suspend_host(host);
b70a7fab 298 if (!ret)
06960a1b 299 clk_disable(sdhci->clk);
b70a7fab
VK
300
301 return ret;
302}
303
304static int sdhci_resume(struct device *dev)
305{
306 struct sdhci_host *host = dev_get_drvdata(dev);
307 struct spear_sdhci *sdhci = dev_get_platdata(dev);
308 int ret;
309
06960a1b 310 ret = clk_enable(sdhci->clk);
b70a7fab
VK
311 if (ret) {
312 dev_dbg(dev, "Resume: Error enabling clock\n");
313 return ret;
314 }
315
316 return sdhci_resume_host(host);
317}
b70a7fab
VK
318#endif
319
4b1a6170
SH
320static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
321
067bf748
VK
322#ifdef CONFIG_OF
323static const struct of_device_id sdhci_spear_id_table[] = {
324 { .compatible = "st,spear300-sdhci" },
325 {}
326};
327MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
328#endif
329
c63b3cba
VK
330static struct platform_driver sdhci_driver = {
331 .driver = {
332 .name = "sdhci",
333 .owner = THIS_MODULE,
b70a7fab 334 .pm = &sdhci_pm_ops,
067bf748 335 .of_match_table = of_match_ptr(sdhci_spear_id_table),
c63b3cba
VK
336 },
337 .probe = sdhci_probe,
0433c143 338 .remove = sdhci_remove,
c63b3cba
VK
339};
340
d1f81a64 341module_platform_driver(sdhci_driver);
c63b3cba
VK
342
343MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
10d8935f 344MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
c63b3cba 345MODULE_LICENSE("GPL v2");
This page took 0.240848 seconds and 5 git commands to generate.