xref: /openbmc/linux/drivers/mmc/host/sdhci-spear.c (revision da89947b)
1c63b3cbaSViresh KUMAR /*
2c63b3cbaSViresh KUMAR  * drivers/mmc/host/sdhci-spear.c
3c63b3cbaSViresh KUMAR  *
4c63b3cbaSViresh KUMAR  * Support of SDHCI platform devices for spear soc family
5c63b3cbaSViresh KUMAR  *
6c63b3cbaSViresh KUMAR  * Copyright (C) 2010 ST Microelectronics
7da89947bSViresh Kumar  * Viresh Kumar <vireshk@kernel.org>
8c63b3cbaSViresh KUMAR  *
9c63b3cbaSViresh KUMAR  * Inspired by sdhci-pltfm.c
10c63b3cbaSViresh KUMAR  *
11c63b3cbaSViresh KUMAR  * This file is licensed under the terms of the GNU General Public
12c63b3cbaSViresh KUMAR  * License version 2. This program is licensed "as is" without any
13c63b3cbaSViresh KUMAR  * warranty of any kind, whether express or implied.
14c63b3cbaSViresh KUMAR  */
15c63b3cbaSViresh KUMAR 
16c63b3cbaSViresh KUMAR #include <linux/clk.h>
17c63b3cbaSViresh KUMAR #include <linux/delay.h>
18c63b3cbaSViresh KUMAR #include <linux/gpio.h>
19c63b3cbaSViresh KUMAR #include <linux/highmem.h>
2088b47679SPaul Gortmaker #include <linux/module.h>
21c63b3cbaSViresh KUMAR #include <linux/interrupt.h>
22c63b3cbaSViresh KUMAR #include <linux/irq.h>
23067bf748SViresh Kumar #include <linux/of.h>
24067bf748SViresh Kumar #include <linux/of_gpio.h>
25c63b3cbaSViresh KUMAR #include <linux/platform_device.h>
26b70a7fabSViresh Kumar #include <linux/pm.h>
27c63b3cbaSViresh KUMAR #include <linux/slab.h>
28c63b3cbaSViresh KUMAR #include <linux/mmc/host.h>
29b42b9b12SRussell King #include <linux/mmc/slot-gpio.h>
30c63b3cbaSViresh KUMAR #include <linux/io.h>
31c63b3cbaSViresh KUMAR #include "sdhci.h"
32c63b3cbaSViresh KUMAR 
33c63b3cbaSViresh KUMAR struct spear_sdhci {
34c63b3cbaSViresh KUMAR 	struct clk *clk;
3503a6d291SUlf Hansson 	int card_int_gpio;
36c63b3cbaSViresh KUMAR };
37c63b3cbaSViresh KUMAR 
38c63b3cbaSViresh KUMAR /* sdhci ops */
39c915568dSLars-Peter Clausen static const struct sdhci_ops sdhci_pltfm_ops = {
401771059cSRussell King 	.set_clock = sdhci_set_clock,
412317f56cSRussell King 	.set_bus_width = sdhci_set_bus_width,
4203231f9bSRussell King 	.reset = sdhci_reset,
4396d7b78cSRussell King 	.set_uhs_signaling = sdhci_set_uhs_signaling,
44c63b3cbaSViresh KUMAR };
45c63b3cbaSViresh KUMAR 
4603a6d291SUlf Hansson static void sdhci_probe_config_dt(struct device_node *np,
4703a6d291SUlf Hansson 				struct spear_sdhci *host)
48067bf748SViresh Kumar {
49067bf748SViresh Kumar 	int cd_gpio;
50067bf748SViresh Kumar 
51067bf748SViresh Kumar 	cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
52067bf748SViresh Kumar 	if (!gpio_is_valid(cd_gpio))
53067bf748SViresh Kumar 		cd_gpio = -1;
54067bf748SViresh Kumar 
5503a6d291SUlf Hansson 	host->card_int_gpio = cd_gpio;
56067bf748SViresh Kumar }
57067bf748SViresh Kumar 
58c3be1efdSBill Pemberton static int sdhci_probe(struct platform_device *pdev)
59c63b3cbaSViresh KUMAR {
60c63b3cbaSViresh KUMAR 	struct sdhci_host *host;
61c63b3cbaSViresh KUMAR 	struct resource *iomem;
62c63b3cbaSViresh KUMAR 	struct spear_sdhci *sdhci;
63fcdb7c8fSRussell King 	struct device *dev;
64c63b3cbaSViresh KUMAR 	int ret;
65c63b3cbaSViresh KUMAR 
66fcdb7c8fSRussell King 	dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
67fcdb7c8fSRussell King 	host = sdhci_alloc_host(dev, sizeof(*sdhci));
68fcdb7c8fSRussell King 	if (IS_ERR(host)) {
69fcdb7c8fSRussell King 		ret = PTR_ERR(host);
70c63b3cbaSViresh KUMAR 		dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
716ebaf8f2SViresh Kumar 		goto err;
72c63b3cbaSViresh KUMAR 	}
73c63b3cbaSViresh KUMAR 
74475d9e3eSRussell King 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
75475d9e3eSRussell King 	host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
76475d9e3eSRussell King 	if (IS_ERR(host->ioaddr)) {
77475d9e3eSRussell King 		ret = PTR_ERR(host->ioaddr);
78475d9e3eSRussell King 		dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
79475d9e3eSRussell King 		goto err_host;
80475d9e3eSRussell King 	}
81475d9e3eSRussell King 
82475d9e3eSRussell King 	host->hw_name = "sdhci";
83475d9e3eSRussell King 	host->ops = &sdhci_pltfm_ops;
84475d9e3eSRussell King 	host->irq = platform_get_irq(pdev, 0);
85475d9e3eSRussell King 	host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
86475d9e3eSRussell King 
87fcdb7c8fSRussell King 	sdhci = sdhci_priv(host);
88fcdb7c8fSRussell King 
89c63b3cbaSViresh KUMAR 	/* clk enable */
90142dbab9SRussell King 	sdhci->clk = devm_clk_get(&pdev->dev, NULL);
91c63b3cbaSViresh KUMAR 	if (IS_ERR(sdhci->clk)) {
92c63b3cbaSViresh KUMAR 		ret = PTR_ERR(sdhci->clk);
93c63b3cbaSViresh KUMAR 		dev_dbg(&pdev->dev, "Error getting clock\n");
94fcdb7c8fSRussell King 		goto err_host;
95c63b3cbaSViresh KUMAR 	}
96c63b3cbaSViresh KUMAR 
97da764f97SViresh Kumar 	ret = clk_prepare_enable(sdhci->clk);
98c63b3cbaSViresh KUMAR 	if (ret) {
99c63b3cbaSViresh KUMAR 		dev_dbg(&pdev->dev, "Error enabling clock\n");
100fcdb7c8fSRussell King 		goto err_host;
101c63b3cbaSViresh KUMAR 	}
102c63b3cbaSViresh KUMAR 
103257f9df1SVipul Kumar Samar 	ret = clk_set_rate(sdhci->clk, 50000000);
104257f9df1SVipul Kumar Samar 	if (ret)
105257f9df1SVipul Kumar Samar 		dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
106257f9df1SVipul Kumar Samar 				clk_get_rate(sdhci->clk));
107257f9df1SVipul Kumar Samar 
10803a6d291SUlf Hansson 	sdhci_probe_config_dt(pdev->dev.of_node, sdhci);
109b42b9b12SRussell King 	/*
110b42b9b12SRussell King 	 * It is optional to use GPIOs for sdhci card detection. If
11103a6d291SUlf Hansson 	 * sdhci->card_int_gpio < 0, then use original sdhci lines otherwise
112b42b9b12SRussell King 	 * GPIO lines. We use the built-in GPIO support for this.
113b42b9b12SRussell King 	 */
11403a6d291SUlf Hansson 	if (sdhci->card_int_gpio >= 0) {
11503a6d291SUlf Hansson 		ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0);
116b42b9b12SRussell King 		if (ret < 0) {
117b42b9b12SRussell King 			dev_dbg(&pdev->dev,
118b42b9b12SRussell King 				"failed to request card-detect gpio%d\n",
11903a6d291SUlf Hansson 				sdhci->card_int_gpio);
120b42b9b12SRussell King 			goto disable_clk;
121b42b9b12SRussell King 		}
122b42b9b12SRussell King 	}
123b42b9b12SRussell King 
124c63b3cbaSViresh KUMAR 	ret = sdhci_add_host(host);
125c63b3cbaSViresh KUMAR 	if (ret) {
126c63b3cbaSViresh KUMAR 		dev_dbg(&pdev->dev, "error adding host\n");
127fcdb7c8fSRussell King 		goto disable_clk;
128c63b3cbaSViresh KUMAR 	}
129c63b3cbaSViresh KUMAR 
130c63b3cbaSViresh KUMAR 	platform_set_drvdata(pdev, host);
131c63b3cbaSViresh KUMAR 
132c63b3cbaSViresh KUMAR 	return 0;
133c63b3cbaSViresh KUMAR 
1346ebaf8f2SViresh Kumar disable_clk:
135da764f97SViresh Kumar 	clk_disable_unprepare(sdhci->clk);
136fcdb7c8fSRussell King err_host:
137fcdb7c8fSRussell King 	sdhci_free_host(host);
138c63b3cbaSViresh KUMAR err:
139c63b3cbaSViresh KUMAR 	dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
140c63b3cbaSViresh KUMAR 	return ret;
141c63b3cbaSViresh KUMAR }
142c63b3cbaSViresh KUMAR 
1436e0ee714SBill Pemberton static int sdhci_remove(struct platform_device *pdev)
144c63b3cbaSViresh KUMAR {
145c63b3cbaSViresh KUMAR 	struct sdhci_host *host = platform_get_drvdata(pdev);
146fcdb7c8fSRussell King 	struct spear_sdhci *sdhci = sdhci_priv(host);
1476ebaf8f2SViresh Kumar 	int dead = 0;
148c63b3cbaSViresh KUMAR 	u32 scratch;
149c63b3cbaSViresh KUMAR 
150c63b3cbaSViresh KUMAR 	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
151c63b3cbaSViresh KUMAR 	if (scratch == (u32)-1)
152c63b3cbaSViresh KUMAR 		dead = 1;
153c63b3cbaSViresh KUMAR 
154c63b3cbaSViresh KUMAR 	sdhci_remove_host(host, dead);
155da764f97SViresh Kumar 	clk_disable_unprepare(sdhci->clk);
156fcdb7c8fSRussell King 	sdhci_free_host(host);
157c63b3cbaSViresh KUMAR 
158c63b3cbaSViresh KUMAR 	return 0;
159c63b3cbaSViresh KUMAR }
160c63b3cbaSViresh KUMAR 
161b0dd099cSJingoo Han #ifdef CONFIG_PM_SLEEP
162b70a7fabSViresh Kumar static int sdhci_suspend(struct device *dev)
163b70a7fabSViresh Kumar {
164b70a7fabSViresh Kumar 	struct sdhci_host *host = dev_get_drvdata(dev);
165fcdb7c8fSRussell King 	struct spear_sdhci *sdhci = sdhci_priv(host);
166b70a7fabSViresh Kumar 	int ret;
167b70a7fabSViresh Kumar 
168984589e5SViresh Kumar 	ret = sdhci_suspend_host(host);
169b70a7fabSViresh Kumar 	if (!ret)
17006960a1bSViresh Kumar 		clk_disable(sdhci->clk);
171b70a7fabSViresh Kumar 
172b70a7fabSViresh Kumar 	return ret;
173b70a7fabSViresh Kumar }
174b70a7fabSViresh Kumar 
175b70a7fabSViresh Kumar static int sdhci_resume(struct device *dev)
176b70a7fabSViresh Kumar {
177b70a7fabSViresh Kumar 	struct sdhci_host *host = dev_get_drvdata(dev);
178fcdb7c8fSRussell King 	struct spear_sdhci *sdhci = sdhci_priv(host);
179b70a7fabSViresh Kumar 	int ret;
180b70a7fabSViresh Kumar 
18106960a1bSViresh Kumar 	ret = clk_enable(sdhci->clk);
182b70a7fabSViresh Kumar 	if (ret) {
183b70a7fabSViresh Kumar 		dev_dbg(dev, "Resume: Error enabling clock\n");
184b70a7fabSViresh Kumar 		return ret;
185b70a7fabSViresh Kumar 	}
186b70a7fabSViresh Kumar 
187b70a7fabSViresh Kumar 	return sdhci_resume_host(host);
188b70a7fabSViresh Kumar }
189b70a7fabSViresh Kumar #endif
190b70a7fabSViresh Kumar 
1914b1a6170SShiraz Hashim static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
1924b1a6170SShiraz Hashim 
193067bf748SViresh Kumar #ifdef CONFIG_OF
194067bf748SViresh Kumar static const struct of_device_id sdhci_spear_id_table[] = {
195067bf748SViresh Kumar 	{ .compatible = "st,spear300-sdhci" },
196067bf748SViresh Kumar 	{}
197067bf748SViresh Kumar };
198067bf748SViresh Kumar MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
199067bf748SViresh Kumar #endif
200067bf748SViresh Kumar 
201c63b3cbaSViresh KUMAR static struct platform_driver sdhci_driver = {
202c63b3cbaSViresh KUMAR 	.driver = {
203c63b3cbaSViresh KUMAR 		.name	= "sdhci",
204b70a7fabSViresh Kumar 		.pm	= &sdhci_pm_ops,
205067bf748SViresh Kumar 		.of_match_table = of_match_ptr(sdhci_spear_id_table),
206c63b3cbaSViresh KUMAR 	},
207c63b3cbaSViresh KUMAR 	.probe		= sdhci_probe,
2080433c143SBill Pemberton 	.remove		= sdhci_remove,
209c63b3cbaSViresh KUMAR };
210c63b3cbaSViresh KUMAR 
211d1f81a64SAxel Lin module_platform_driver(sdhci_driver);
212c63b3cbaSViresh KUMAR 
213c63b3cbaSViresh KUMAR MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
214da89947bSViresh Kumar MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>");
215c63b3cbaSViresh KUMAR MODULE_LICENSE("GPL v2");
216