xref: /openbmc/linux/drivers/mmc/host/sdhci-spear.c (revision 1771059cf5f9c09e37ef6315df8acf120f2642fc)
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.linux@gmail.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/of.h>
24 #include <linux/of_gpio.h>
25 #include <linux/platform_device.h>
26 #include <linux/pm.h>
27 #include <linux/slab.h>
28 #include <linux/mmc/host.h>
29 #include <linux/mmc/sdhci-spear.h>
30 #include <linux/mmc/slot-gpio.h>
31 #include <linux/io.h>
32 #include "sdhci.h"
33 
34 struct spear_sdhci {
35 	struct clk *clk;
36 	struct sdhci_plat_data *data;
37 };
38 
39 /* sdhci ops */
40 static const struct sdhci_ops sdhci_pltfm_ops = {
41 	.set_clock = sdhci_set_clock,
42 	.set_bus_width = sdhci_set_bus_width,
43 	.reset = sdhci_reset,
44 };
45 
46 #ifdef CONFIG_OF
47 static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
48 {
49 	struct device_node *np = pdev->dev.of_node;
50 	struct sdhci_plat_data *pdata = NULL;
51 	int cd_gpio;
52 
53 	cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
54 	if (!gpio_is_valid(cd_gpio))
55 		cd_gpio = -1;
56 
57 	/* If pdata is required */
58 	if (cd_gpio != -1) {
59 		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
60 		if (!pdata)
61 			dev_err(&pdev->dev, "DT: kzalloc failed\n");
62 		else
63 			pdata->card_int_gpio = cd_gpio;
64 	}
65 
66 	return pdata;
67 }
68 #else
69 static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
70 {
71 	return ERR_PTR(-ENOSYS);
72 }
73 #endif
74 
75 static int sdhci_probe(struct platform_device *pdev)
76 {
77 	struct device_node *np = pdev->dev.of_node;
78 	struct sdhci_host *host;
79 	struct resource *iomem;
80 	struct spear_sdhci *sdhci;
81 	struct device *dev;
82 	int ret;
83 
84 	dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
85 	host = sdhci_alloc_host(dev, sizeof(*sdhci));
86 	if (IS_ERR(host)) {
87 		ret = PTR_ERR(host);
88 		dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
89 		goto err;
90 	}
91 
92 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
93 	host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
94 	if (IS_ERR(host->ioaddr)) {
95 		ret = PTR_ERR(host->ioaddr);
96 		dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
97 		goto err_host;
98 	}
99 
100 	host->hw_name = "sdhci";
101 	host->ops = &sdhci_pltfm_ops;
102 	host->irq = platform_get_irq(pdev, 0);
103 	host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
104 
105 	sdhci = sdhci_priv(host);
106 
107 	/* clk enable */
108 	sdhci->clk = devm_clk_get(&pdev->dev, NULL);
109 	if (IS_ERR(sdhci->clk)) {
110 		ret = PTR_ERR(sdhci->clk);
111 		dev_dbg(&pdev->dev, "Error getting clock\n");
112 		goto err_host;
113 	}
114 
115 	ret = clk_prepare_enable(sdhci->clk);
116 	if (ret) {
117 		dev_dbg(&pdev->dev, "Error enabling clock\n");
118 		goto err_host;
119 	}
120 
121 	ret = clk_set_rate(sdhci->clk, 50000000);
122 	if (ret)
123 		dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
124 				clk_get_rate(sdhci->clk));
125 
126 	if (np) {
127 		sdhci->data = sdhci_probe_config_dt(pdev);
128 		if (IS_ERR(sdhci->data)) {
129 			dev_err(&pdev->dev, "DT: Failed to get pdata\n");
130 			goto disable_clk;
131 		}
132 	} else {
133 		sdhci->data = dev_get_platdata(&pdev->dev);
134 	}
135 
136 	/*
137 	 * It is optional to use GPIOs for sdhci card detection. If
138 	 * sdhci->data is NULL, then use original sdhci lines otherwise
139 	 * GPIO lines. We use the built-in GPIO support for this.
140 	 */
141 	if (sdhci->data && sdhci->data->card_int_gpio >= 0) {
142 		ret = mmc_gpio_request_cd(host->mmc,
143 					  sdhci->data->card_int_gpio, 0);
144 		if (ret < 0) {
145 			dev_dbg(&pdev->dev,
146 				"failed to request card-detect gpio%d\n",
147 				sdhci->data->card_int_gpio);
148 			goto disable_clk;
149 		}
150 	}
151 
152 	ret = sdhci_add_host(host);
153 	if (ret) {
154 		dev_dbg(&pdev->dev, "error adding host\n");
155 		goto disable_clk;
156 	}
157 
158 	platform_set_drvdata(pdev, host);
159 
160 	return 0;
161 
162 disable_clk:
163 	clk_disable_unprepare(sdhci->clk);
164 err_host:
165 	sdhci_free_host(host);
166 err:
167 	dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
168 	return ret;
169 }
170 
171 static int sdhci_remove(struct platform_device *pdev)
172 {
173 	struct sdhci_host *host = platform_get_drvdata(pdev);
174 	struct spear_sdhci *sdhci = sdhci_priv(host);
175 	int dead = 0;
176 	u32 scratch;
177 
178 	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
179 	if (scratch == (u32)-1)
180 		dead = 1;
181 
182 	sdhci_remove_host(host, dead);
183 	clk_disable_unprepare(sdhci->clk);
184 	sdhci_free_host(host);
185 
186 	return 0;
187 }
188 
189 #ifdef CONFIG_PM_SLEEP
190 static int sdhci_suspend(struct device *dev)
191 {
192 	struct sdhci_host *host = dev_get_drvdata(dev);
193 	struct spear_sdhci *sdhci = sdhci_priv(host);
194 	int ret;
195 
196 	ret = sdhci_suspend_host(host);
197 	if (!ret)
198 		clk_disable(sdhci->clk);
199 
200 	return ret;
201 }
202 
203 static int sdhci_resume(struct device *dev)
204 {
205 	struct sdhci_host *host = dev_get_drvdata(dev);
206 	struct spear_sdhci *sdhci = sdhci_priv(host);
207 	int ret;
208 
209 	ret = clk_enable(sdhci->clk);
210 	if (ret) {
211 		dev_dbg(dev, "Resume: Error enabling clock\n");
212 		return ret;
213 	}
214 
215 	return sdhci_resume_host(host);
216 }
217 #endif
218 
219 static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
220 
221 #ifdef CONFIG_OF
222 static const struct of_device_id sdhci_spear_id_table[] = {
223 	{ .compatible = "st,spear300-sdhci" },
224 	{}
225 };
226 MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
227 #endif
228 
229 static struct platform_driver sdhci_driver = {
230 	.driver = {
231 		.name	= "sdhci",
232 		.owner	= THIS_MODULE,
233 		.pm	= &sdhci_pm_ops,
234 		.of_match_table = of_match_ptr(sdhci_spear_id_table),
235 	},
236 	.probe		= sdhci_probe,
237 	.remove		= sdhci_remove,
238 };
239 
240 module_platform_driver(sdhci_driver);
241 
242 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
243 MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
244 MODULE_LICENSE("GPL v2");
245