Merge branch 'for-3.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[firefly-linux-kernel-4.4.55.git] / drivers / mmc / host / sdhci-spear.c
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
7  * Viresh Kumar<viresh.kumar@st.com>
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>
20 #include <linux/module.h>
21 #include <linux/interrupt.h>
22 #include <linux/irq.h>
23 #include <linux/platform_device.h>
24 #include <linux/pm.h>
25 #include <linux/slab.h>
26 #include <linux/mmc/host.h>
27 #include <linux/mmc/sdhci-spear.h>
28 #include <linux/io.h>
29 #include "sdhci.h"
30
31 struct spear_sdhci {
32         struct clk *clk;
33         struct sdhci_plat_data *data;
34 };
35
36 /* sdhci ops */
37 static struct sdhci_ops sdhci_pltfm_ops = {
38         /* Nothing to do for now. */
39 };
40
41 /* gpio card detection interrupt handler */
42 static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
43 {
44         struct platform_device *pdev = dev_id;
45         struct sdhci_host *host = platform_get_drvdata(pdev);
46         struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
47         unsigned long gpio_irq_type;
48         int val;
49
50         val = gpio_get_value(sdhci->data->card_int_gpio);
51
52         /* val == 1 -> card removed, val == 0 -> card inserted */
53         /* if card removed - set irq for low level, else vice versa */
54         gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
55         irq_set_irq_type(irq, gpio_irq_type);
56
57         if (sdhci->data->card_power_gpio >= 0) {
58                 if (!sdhci->data->power_always_enb) {
59                         /* if card inserted, give power, otherwise remove it */
60                         val = sdhci->data->power_active_high ? !val : val ;
61                         gpio_set_value(sdhci->data->card_power_gpio, val);
62                 }
63         }
64
65         /* inform sdhci driver about card insertion/removal */
66         tasklet_schedule(&host->card_tasklet);
67
68         return IRQ_HANDLED;
69 }
70
71 static int __devinit sdhci_probe(struct platform_device *pdev)
72 {
73         struct sdhci_host *host;
74         struct resource *iomem;
75         struct spear_sdhci *sdhci;
76         int ret;
77
78         iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
79         if (!iomem) {
80                 ret = -ENOMEM;
81                 dev_dbg(&pdev->dev, "memory resource not defined\n");
82                 goto err;
83         }
84
85         if (!devm_request_mem_region(&pdev->dev, iomem->start,
86                                 resource_size(iomem), "spear-sdhci")) {
87                 ret = -EBUSY;
88                 dev_dbg(&pdev->dev, "cannot request region\n");
89                 goto err;
90         }
91
92         sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
93         if (!sdhci) {
94                 ret = -ENOMEM;
95                 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
96                 goto err;
97         }
98
99         /* clk enable */
100         sdhci->clk = clk_get(&pdev->dev, NULL);
101         if (IS_ERR(sdhci->clk)) {
102                 ret = PTR_ERR(sdhci->clk);
103                 dev_dbg(&pdev->dev, "Error getting clock\n");
104                 goto err;
105         }
106
107         ret = clk_enable(sdhci->clk);
108         if (ret) {
109                 dev_dbg(&pdev->dev, "Error enabling clock\n");
110                 goto put_clk;
111         }
112
113         /* overwrite platform_data */
114         sdhci->data = dev_get_platdata(&pdev->dev);
115         pdev->dev.platform_data = sdhci;
116
117         if (pdev->dev.parent)
118                 host = sdhci_alloc_host(pdev->dev.parent, 0);
119         else
120                 host = sdhci_alloc_host(&pdev->dev, 0);
121
122         if (IS_ERR(host)) {
123                 ret = PTR_ERR(host);
124                 dev_dbg(&pdev->dev, "error allocating host\n");
125                 goto disable_clk;
126         }
127
128         host->hw_name = "sdhci";
129         host->ops = &sdhci_pltfm_ops;
130         host->irq = platform_get_irq(pdev, 0);
131         host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
132
133         host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
134                         resource_size(iomem));
135         if (!host->ioaddr) {
136                 ret = -ENOMEM;
137                 dev_dbg(&pdev->dev, "failed to remap registers\n");
138                 goto free_host;
139         }
140
141         ret = sdhci_add_host(host);
142         if (ret) {
143                 dev_dbg(&pdev->dev, "error adding host\n");
144                 goto free_host;
145         }
146
147         platform_set_drvdata(pdev, host);
148
149         /*
150          * It is optional to use GPIOs for sdhci Power control & sdhci card
151          * interrupt detection. If sdhci->data is NULL, then use original sdhci
152          * lines otherwise GPIO lines.
153          * If GPIO is selected for power control, then power should be disabled
154          * after card removal and should be enabled when card insertion
155          * interrupt occurs
156          */
157         if (!sdhci->data)
158                 return 0;
159
160         if (sdhci->data->card_power_gpio >= 0) {
161                 int val = 0;
162
163                 ret = devm_gpio_request(&pdev->dev,
164                                 sdhci->data->card_power_gpio, "sdhci");
165                 if (ret < 0) {
166                         dev_dbg(&pdev->dev, "gpio request fail: %d\n",
167                                         sdhci->data->card_power_gpio);
168                         goto set_drvdata;
169                 }
170
171                 if (sdhci->data->power_always_enb)
172                         val = sdhci->data->power_active_high;
173                 else
174                         val = !sdhci->data->power_active_high;
175
176                 ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
177                 if (ret) {
178                         dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
179                                         sdhci->data->card_power_gpio);
180                         goto set_drvdata;
181                 }
182         }
183
184         if (sdhci->data->card_int_gpio >= 0) {
185                 ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
186                                 "sdhci");
187                 if (ret < 0) {
188                         dev_dbg(&pdev->dev, "gpio request fail: %d\n",
189                                         sdhci->data->card_int_gpio);
190                         goto set_drvdata;
191                 }
192
193                 ret = gpio_direction_input(sdhci->data->card_int_gpio);
194                 if (ret) {
195                         dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
196                                         sdhci->data->card_int_gpio);
197                         goto set_drvdata;
198                 }
199                 ret = devm_request_irq(&pdev->dev,
200                                 gpio_to_irq(sdhci->data->card_int_gpio),
201                                 sdhci_gpio_irq, IRQF_TRIGGER_LOW,
202                                 mmc_hostname(host->mmc), pdev);
203                 if (ret) {
204                         dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
205                                         sdhci->data->card_int_gpio);
206                         goto set_drvdata;
207                 }
208
209         }
210
211         return 0;
212
213 set_drvdata:
214         platform_set_drvdata(pdev, NULL);
215         sdhci_remove_host(host, 1);
216 free_host:
217         sdhci_free_host(host);
218 disable_clk:
219         clk_disable(sdhci->clk);
220 put_clk:
221         clk_put(sdhci->clk);
222 err:
223         dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
224         return ret;
225 }
226
227 static int __devexit sdhci_remove(struct platform_device *pdev)
228 {
229         struct sdhci_host *host = platform_get_drvdata(pdev);
230         struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
231         int dead = 0;
232         u32 scratch;
233
234         platform_set_drvdata(pdev, NULL);
235         scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
236         if (scratch == (u32)-1)
237                 dead = 1;
238
239         sdhci_remove_host(host, dead);
240         sdhci_free_host(host);
241         clk_disable(sdhci->clk);
242         clk_put(sdhci->clk);
243
244         return 0;
245 }
246
247 #ifdef CONFIG_PM
248 static int sdhci_suspend(struct device *dev)
249 {
250         struct sdhci_host *host = dev_get_drvdata(dev);
251         struct spear_sdhci *sdhci = dev_get_platdata(dev);
252         int ret;
253
254         ret = sdhci_suspend_host(host);
255         if (!ret)
256                 clk_disable(sdhci->clk);
257
258         return ret;
259 }
260
261 static int sdhci_resume(struct device *dev)
262 {
263         struct sdhci_host *host = dev_get_drvdata(dev);
264         struct spear_sdhci *sdhci = dev_get_platdata(dev);
265         int ret;
266
267         ret = clk_enable(sdhci->clk);
268         if (ret) {
269                 dev_dbg(dev, "Resume: Error enabling clock\n");
270                 return ret;
271         }
272
273         return sdhci_resume_host(host);
274 }
275 #endif
276
277 static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
278
279 static struct platform_driver sdhci_driver = {
280         .driver = {
281                 .name   = "sdhci",
282                 .owner  = THIS_MODULE,
283                 .pm     = &sdhci_pm_ops,
284         },
285         .probe          = sdhci_probe,
286         .remove         = __devexit_p(sdhci_remove),
287 };
288
289 module_platform_driver(sdhci_driver);
290
291 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
292 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
293 MODULE_LICENSE("GPL v2");