[go: nahoru, domu]

1/*
2 * SDHCI support for SiRF primaII and marco SoCs
3 *
4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/delay.h>
10#include <linux/device.h>
11#include <linux/mmc/host.h>
12#include <linux/module.h>
13#include <linux/of.h>
14#include <linux/of_gpio.h>
15#include <linux/mmc/slot-gpio.h>
16#include "sdhci-pltfm.h"
17
18#define SDHCI_SIRF_8BITBUS BIT(3)
19
20struct sdhci_sirf_priv {
21	struct clk *clk;
22	int gpio_cd;
23};
24
25static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
26{
27	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
28	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
29	return clk_get_rate(priv->clk);
30}
31
32static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
33{
34	u8 ctrl;
35
36	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
37	ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS);
38
39	/*
40	 * CSR atlas7 and prima2 SD host version is not 3.0
41	 * 8bit-width enable bit of CSR SD hosts is 3,
42	 * while stardard hosts use bit 5
43	 */
44	if (width == MMC_BUS_WIDTH_8)
45		ctrl |= SDHCI_SIRF_8BITBUS;
46	else if (width == MMC_BUS_WIDTH_4)
47		ctrl |= SDHCI_CTRL_4BITBUS;
48
49	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
50}
51
52static struct sdhci_ops sdhci_sirf_ops = {
53	.set_clock = sdhci_set_clock,
54	.get_max_clock	= sdhci_sirf_get_max_clk,
55	.set_bus_width = sdhci_sirf_set_bus_width,
56	.reset = sdhci_reset,
57	.set_uhs_signaling = sdhci_set_uhs_signaling,
58};
59
60static struct sdhci_pltfm_data sdhci_sirf_pdata = {
61	.ops = &sdhci_sirf_ops,
62	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
63		SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
64		SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
65		SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
66		SDHCI_QUIRK_DELAY_AFTER_POWER,
67};
68
69static int sdhci_sirf_probe(struct platform_device *pdev)
70{
71	struct sdhci_host *host;
72	struct sdhci_pltfm_host *pltfm_host;
73	struct sdhci_sirf_priv *priv;
74	struct clk *clk;
75	int gpio_cd;
76	int ret;
77
78	clk = devm_clk_get(&pdev->dev, NULL);
79	if (IS_ERR(clk)) {
80		dev_err(&pdev->dev, "unable to get clock");
81		return PTR_ERR(clk);
82	}
83
84	if (pdev->dev.of_node)
85		gpio_cd = of_get_named_gpio(pdev->dev.of_node, "cd-gpios", 0);
86	else
87		gpio_cd = -EINVAL;
88
89	host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, sizeof(struct sdhci_sirf_priv));
90	if (IS_ERR(host))
91		return PTR_ERR(host);
92
93	pltfm_host = sdhci_priv(host);
94	priv = sdhci_pltfm_priv(pltfm_host);
95	priv->clk = clk;
96	priv->gpio_cd = gpio_cd;
97
98	sdhci_get_of_property(pdev);
99
100	ret = clk_prepare_enable(priv->clk);
101	if (ret)
102		goto err_clk_prepare;
103
104	ret = sdhci_add_host(host);
105	if (ret)
106		goto err_sdhci_add;
107
108	/*
109	 * We must request the IRQ after sdhci_add_host(), as the tasklet only
110	 * gets setup in sdhci_add_host() and we oops.
111	 */
112	if (gpio_is_valid(priv->gpio_cd)) {
113		ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd, 0);
114		if (ret) {
115			dev_err(&pdev->dev, "card detect irq request failed: %d\n",
116				ret);
117			goto err_request_cd;
118		}
119		mmc_gpiod_request_cd_irq(host->mmc);
120	}
121
122	return 0;
123
124err_request_cd:
125	sdhci_remove_host(host, 0);
126err_sdhci_add:
127	clk_disable_unprepare(priv->clk);
128err_clk_prepare:
129	sdhci_pltfm_free(pdev);
130	return ret;
131}
132
133static int sdhci_sirf_remove(struct platform_device *pdev)
134{
135	struct sdhci_host *host = platform_get_drvdata(pdev);
136	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
137	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
138
139	sdhci_pltfm_unregister(pdev);
140
141	if (gpio_is_valid(priv->gpio_cd))
142		mmc_gpio_free_cd(host->mmc);
143
144	clk_disable_unprepare(priv->clk);
145	return 0;
146}
147
148#ifdef CONFIG_PM_SLEEP
149static int sdhci_sirf_suspend(struct device *dev)
150{
151	struct sdhci_host *host = dev_get_drvdata(dev);
152	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
153	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
154	int ret;
155
156	ret = sdhci_suspend_host(host);
157	if (ret)
158		return ret;
159
160	clk_disable(priv->clk);
161
162	return 0;
163}
164
165static int sdhci_sirf_resume(struct device *dev)
166{
167	struct sdhci_host *host = dev_get_drvdata(dev);
168	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
169	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
170	int ret;
171
172	ret = clk_enable(priv->clk);
173	if (ret) {
174		dev_dbg(dev, "Resume: Error enabling clock\n");
175		return ret;
176	}
177
178	return sdhci_resume_host(host);
179}
180
181static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
182#endif
183
184static const struct of_device_id sdhci_sirf_of_match[] = {
185	{ .compatible = "sirf,prima2-sdhc" },
186	{ }
187};
188MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
189
190static struct platform_driver sdhci_sirf_driver = {
191	.driver		= {
192		.name	= "sdhci-sirf",
193		.of_match_table = sdhci_sirf_of_match,
194#ifdef CONFIG_PM_SLEEP
195		.pm	= &sdhci_sirf_pm_ops,
196#endif
197	},
198	.probe		= sdhci_sirf_probe,
199	.remove		= sdhci_sirf_remove,
200};
201
202module_platform_driver(sdhci_sirf_driver);
203
204MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
205MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
206MODULE_LICENSE("GPL v2");
207