1c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR/*
2c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * drivers/mmc/host/sdhci-spear.c
3c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR *
4c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * Support of SDHCI platform devices for spear soc family
5c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR *
6c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * Copyright (C) 2010 ST Microelectronics
7c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * Viresh Kumar<viresh.kumar@st.com>
8c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR *
9c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * Inspired by sdhci-pltfm.c
10c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR *
11c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * This file is licensed under the terms of the GNU General Public
12c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * License version 2. This program is licensed "as is" without any
13c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR * warranty of any kind, whether express or implied.
14c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR */
15c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
16c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/clk.h>
17c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/delay.h>
18c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/gpio.h>
19c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/highmem.h>
2088b47679746b81534002bcba42da97ab82b5d12aPaul Gortmaker#include <linux/module.h>
21c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/interrupt.h>
22c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/irq.h>
23c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/platform_device.h>
24b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar#include <linux/pm.h>
25c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/slab.h>
26c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/mmc/host.h>
27c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/mmc/sdhci-spear.h>
28c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include <linux/io.h>
29c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR#include "sdhci.h"
30c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
31c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARstruct spear_sdhci {
32c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct clk *clk;
33c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct sdhci_plat_data *data;
34c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR};
35c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
36c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR/* sdhci ops */
37c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARstatic struct sdhci_ops sdhci_pltfm_ops = {
38c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/* Nothing to do for now. */
39c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR};
40c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
41c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR/* gpio card detection interrupt handler */
42c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARstatic irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
43c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR{
44c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct platform_device *pdev = dev_id;
45c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct sdhci_host *host = platform_get_drvdata(pdev);
46c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
47c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	unsigned long gpio_irq_type;
48c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	int val;
49c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
50c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	val = gpio_get_value(sdhci->data->card_int_gpio);
51c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
52c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/* val == 1 -> card removed, val == 0 -> card inserted */
53c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/* if card removed - set irq for low level, else vice versa */
54c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
55dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner	irq_set_irq_type(irq, gpio_irq_type);
56c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
57c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (sdhci->data->card_power_gpio >= 0) {
58c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (!sdhci->data->power_always_enb) {
59c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			/* if card inserted, give power, otherwise remove it */
60c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			val = sdhci->data->power_active_high ? !val : val ;
61c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			gpio_set_value(sdhci->data->card_power_gpio, val);
62c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
63c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
64c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
65c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/* inform sdhci driver about card insertion/removal */
66c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	tasklet_schedule(&host->card_tasklet);
67c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
68c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	return IRQ_HANDLED;
69c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR}
70c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
71c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARstatic int __devinit sdhci_probe(struct platform_device *pdev)
72c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR{
73c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct sdhci_host *host;
74c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct resource *iomem;
75c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct spear_sdhci *sdhci;
76c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	int ret;
77c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
78c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	BUG_ON(pdev == NULL);
79c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
80c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
81c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (!iomem) {
82c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = -ENOMEM;
83c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "memory resource not defined\n");
84c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err;
85c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
86c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
87c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (!request_mem_region(iomem->start, resource_size(iomem),
88c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR				"spear-sdhci")) {
89c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = -EBUSY;
90c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "cannot request region\n");
91c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err;
92c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
93c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
94c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL);
95c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (!sdhci) {
96c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = -ENOMEM;
97c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
98c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err_kzalloc;
99c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
100c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
101c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/* clk enable */
102c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci->clk = clk_get(&pdev->dev, NULL);
103c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (IS_ERR(sdhci->clk)) {
104c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = PTR_ERR(sdhci->clk);
105c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "Error getting clock\n");
106c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err_clk_get;
107c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
108c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
109c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	ret = clk_enable(sdhci->clk);
110c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (ret) {
111c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "Error enabling clock\n");
112c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err_clk_enb;
113c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
114c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
115c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/* overwrite platform_data */
116c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci->data = dev_get_platdata(&pdev->dev);
117c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	pdev->dev.platform_data = sdhci;
118c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
119c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (pdev->dev.parent)
120c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		host = sdhci_alloc_host(pdev->dev.parent, 0);
121c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	else
122c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		host = sdhci_alloc_host(&pdev->dev, 0);
123c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
124c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (IS_ERR(host)) {
125c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = PTR_ERR(host);
126c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "error allocating host\n");
127c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err_alloc_host;
128c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
129c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
130c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	host->hw_name = "sdhci";
131c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	host->ops = &sdhci_pltfm_ops;
132c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	host->irq = platform_get_irq(pdev, 0);
133c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
134c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
135c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	host->ioaddr = ioremap(iomem->start, resource_size(iomem));
136c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (!host->ioaddr) {
137c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = -ENOMEM;
138c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "failed to remap registers\n");
139c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err_ioremap;
140c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
141c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
142c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	ret = sdhci_add_host(host);
143c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (ret) {
144c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dev_dbg(&pdev->dev, "error adding host\n");
145c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		goto err_add_host;
146c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
147c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
148c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	platform_set_drvdata(pdev, host);
149c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
150c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	/*
151c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 * It is optional to use GPIOs for sdhci Power control & sdhci card
152c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 * interrupt detection. If sdhci->data is NULL, then use original sdhci
153c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 * lines otherwise GPIO lines.
154c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 * If GPIO is selected for power control, then power should be disabled
155c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 * after card removal and should be enabled when card insertion
156c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 * interrupt occurs
157c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	 */
158c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (!sdhci->data)
159c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		return 0;
160c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
161c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (sdhci->data->card_power_gpio >= 0) {
162c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		int val = 0;
163c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
164c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = gpio_request(sdhci->data->card_power_gpio, "sdhci");
165c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (ret < 0) {
166c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			dev_dbg(&pdev->dev, "gpio request fail: %d\n",
167c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR					sdhci->data->card_power_gpio);
168c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			goto err_pgpio_request;
169c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
170c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
171c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (sdhci->data->power_always_enb)
172c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			val = sdhci->data->power_active_high;
173c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		else
174c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			val = !sdhci->data->power_active_high;
175c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
176c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
177c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (ret) {
178c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
179c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR					sdhci->data->card_power_gpio);
180c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			goto err_pgpio_direction;
181c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
182c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
183c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
184c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (sdhci->data->card_int_gpio >= 0) {
185c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = gpio_request(sdhci->data->card_int_gpio, "sdhci");
186c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (ret < 0) {
187c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			dev_dbg(&pdev->dev, "gpio request fail: %d\n",
188c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR					sdhci->data->card_int_gpio);
189c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			goto err_igpio_request;
190c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
191c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
192c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = gpio_direction_input(sdhci->data->card_int_gpio);
193c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (ret) {
194c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
195c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR					sdhci->data->card_int_gpio);
196c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			goto err_igpio_direction;
197c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
198c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		ret = request_irq(gpio_to_irq(sdhci->data->card_int_gpio),
199c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR				sdhci_gpio_irq, IRQF_TRIGGER_LOW,
200c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR				mmc_hostname(host->mmc), pdev);
201c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (ret) {
202c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
203c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR					sdhci->data->card_int_gpio);
204c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			goto err_igpio_request_irq;
205c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
206c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
207c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
208c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
209c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	return 0;
210c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
211c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_igpio_request_irq:
212c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_igpio_direction:
213c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (sdhci->data->card_int_gpio >= 0)
214c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		gpio_free(sdhci->data->card_int_gpio);
215c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_igpio_request:
216c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_pgpio_direction:
217c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (sdhci->data->card_power_gpio >= 0)
218c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		gpio_free(sdhci->data->card_power_gpio);
219c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_pgpio_request:
220c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	platform_set_drvdata(pdev, NULL);
221c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci_remove_host(host, 1);
222c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_add_host:
223c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	iounmap(host->ioaddr);
224c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_ioremap:
225c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci_free_host(host);
226c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_alloc_host:
227c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	clk_disable(sdhci->clk);
228c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_clk_enb:
229c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	clk_put(sdhci->clk);
230c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_clk_get:
231c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	kfree(sdhci);
232c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr_kzalloc:
233c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	release_mem_region(iomem->start, resource_size(iomem));
234c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARerr:
235c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
236c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	return ret;
237c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR}
238c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
239c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARstatic int __devexit sdhci_remove(struct platform_device *pdev)
240c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR{
241c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct sdhci_host *host = platform_get_drvdata(pdev);
242c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
243c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
244c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	int dead;
245c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	u32 scratch;
246c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
247c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (sdhci->data) {
248c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (sdhci->data->card_int_gpio >= 0) {
249c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			free_irq(gpio_to_irq(sdhci->data->card_int_gpio), pdev);
250c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			gpio_free(sdhci->data->card_int_gpio);
251c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		}
252c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
253c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		if (sdhci->data->card_power_gpio >= 0)
254c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR			gpio_free(sdhci->data->card_power_gpio);
255c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	}
256c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
257c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	platform_set_drvdata(pdev, NULL);
258c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	dead = 0;
259c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
260c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (scratch == (u32)-1)
261c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		dead = 1;
262c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
263c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci_remove_host(host, dead);
264c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	iounmap(host->ioaddr);
265c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	sdhci_free_host(host);
266c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	clk_disable(sdhci->clk);
267c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	clk_put(sdhci->clk);
268c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	kfree(sdhci);
269c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	if (iomem)
270c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		release_mem_region(iomem->start, resource_size(iomem));
271c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
272c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	return 0;
273c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR}
274c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
275b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar#ifdef CONFIG_PM
276b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumarstatic int sdhci_suspend(struct device *dev)
277b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar{
278b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	struct sdhci_host *host = dev_get_drvdata(dev);
279b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	struct spear_sdhci *sdhci = dev_get_platdata(dev);
280b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	int ret;
281b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar
282984589e59f5796b4ef9a778c6a1937fc9319c423Viresh Kumar	ret = sdhci_suspend_host(host);
283b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	if (!ret)
284b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar		clk_disable(sdhci->clk);
285b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar
286b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	return ret;
287b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar}
288b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar
289b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumarstatic int sdhci_resume(struct device *dev)
290b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar{
291b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	struct sdhci_host *host = dev_get_drvdata(dev);
292b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	struct spear_sdhci *sdhci = dev_get_platdata(dev);
293b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	int ret;
294b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar
295b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	ret = clk_enable(sdhci->clk);
296b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	if (ret) {
297b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar		dev_dbg(dev, "Resume: Error enabling clock\n");
298b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar		return ret;
299b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	}
300b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar
301b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar	return sdhci_resume_host(host);
302b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar}
303b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar#endif
304b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar
3054b1a61705a563edb951b8fd9734bcb14286a7888Shiraz Hashimstatic SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
3064b1a61705a563edb951b8fd9734bcb14286a7888Shiraz Hashim
307c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARstatic struct platform_driver sdhci_driver = {
308c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	.driver = {
309c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		.name	= "sdhci",
310c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR		.owner	= THIS_MODULE,
311b70a7fab26db65f7daaf04f49a3bd673250f48c7Viresh Kumar		.pm	= &sdhci_pm_ops,
312c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	},
313c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	.probe		= sdhci_probe,
314c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR	.remove		= __devexit_p(sdhci_remove),
315c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR};
316c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
317d1f81a64a4250bdd776978be06ae2b8e13ec7471Axel Linmodule_platform_driver(sdhci_driver);
318c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMAR
319c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARMODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
320c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARMODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
321c63b3cba4f47ef9f4b3f952b4f923cf341d250acViresh KUMARMODULE_LICENSE("GPL v2");
322