xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision b4f3b7c8)
1 /*
2  * Copyright (C) 2010 Google, Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14 
15 #include <linux/err.h>
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/platform_device.h>
19 #include <linux/clk.h>
20 #include <linux/io.h>
21 #include <linux/of.h>
22 #include <linux/of_device.h>
23 #include <linux/of_gpio.h>
24 #include <linux/gpio.h>
25 #include <linux/mmc/card.h>
26 #include <linux/mmc/host.h>
27 #include <linux/mmc/slot-gpio.h>
28 
29 #include <asm/gpio.h>
30 
31 #include "sdhci-pltfm.h"
32 
33 /* Tegra SDHOST controller vendor register definitions */
34 #define SDHCI_TEGRA_VENDOR_MISC_CTRL		0x120
35 #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300	0x20
36 
37 #define NVQUIRK_FORCE_SDHCI_SPEC_200	BIT(0)
38 #define NVQUIRK_ENABLE_BLOCK_GAP_DET	BIT(1)
39 #define NVQUIRK_ENABLE_SDHCI_SPEC_300	BIT(2)
40 
41 struct sdhci_tegra_soc_data {
42 	const struct sdhci_pltfm_data *pdata;
43 	u32 nvquirks;
44 };
45 
46 struct sdhci_tegra {
47 	const struct sdhci_tegra_soc_data *soc_data;
48 	int power_gpio;
49 };
50 
51 static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
52 {
53 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
54 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
55 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
56 
57 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
58 			(reg == SDHCI_HOST_VERSION))) {
59 		/* Erratum: Version register is invalid in HW. */
60 		return SDHCI_SPEC_200;
61 	}
62 
63 	return readw(host->ioaddr + reg);
64 }
65 
66 static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
67 {
68 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
69 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
70 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
71 
72 	/* Seems like we're getting spurious timeout and crc errors, so
73 	 * disable signalling of them. In case of real errors software
74 	 * timers should take care of eventually detecting them.
75 	 */
76 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
77 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
78 
79 	writel(val, host->ioaddr + reg);
80 
81 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
82 			(reg == SDHCI_INT_ENABLE))) {
83 		/* Erratum: Must enable block gap interrupt detection */
84 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
85 		if (val & SDHCI_INT_CARD_INT)
86 			gap_ctrl |= 0x8;
87 		else
88 			gap_ctrl &= ~0x8;
89 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
90 	}
91 }
92 
93 static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
94 {
95 	return mmc_gpio_get_ro(host->mmc);
96 }
97 
98 static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
99 {
100 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
101 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
102 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
103 
104 	sdhci_reset(host, mask);
105 
106 	if (!(mask & SDHCI_RESET_ALL))
107 		return;
108 
109 	/* Erratum: Enable SDHCI spec v3.00 support */
110 	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) {
111 		u32 misc_ctrl;
112 
113 		misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
114 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
115 		sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
116 	}
117 }
118 
119 static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
120 {
121 	u32 ctrl;
122 
123 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
124 	if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
125 	    (bus_width == MMC_BUS_WIDTH_8)) {
126 		ctrl &= ~SDHCI_CTRL_4BITBUS;
127 		ctrl |= SDHCI_CTRL_8BITBUS;
128 	} else {
129 		ctrl &= ~SDHCI_CTRL_8BITBUS;
130 		if (bus_width == MMC_BUS_WIDTH_4)
131 			ctrl |= SDHCI_CTRL_4BITBUS;
132 		else
133 			ctrl &= ~SDHCI_CTRL_4BITBUS;
134 	}
135 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
136 }
137 
138 static const struct sdhci_ops tegra_sdhci_ops = {
139 	.get_ro     = tegra_sdhci_get_ro,
140 	.read_w     = tegra_sdhci_readw,
141 	.write_l    = tegra_sdhci_writel,
142 	.set_clock  = sdhci_set_clock,
143 	.set_bus_width = tegra_sdhci_set_bus_width,
144 	.reset      = tegra_sdhci_reset,
145 	.set_uhs_signaling = sdhci_set_uhs_signaling,
146 };
147 
148 static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
149 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
150 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
151 		  SDHCI_QUIRK_NO_HISPD_BIT |
152 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
153 	.ops  = &tegra_sdhci_ops,
154 };
155 
156 static struct sdhci_tegra_soc_data soc_data_tegra20 = {
157 	.pdata = &sdhci_tegra20_pdata,
158 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
159 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
160 };
161 
162 static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
163 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
164 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
165 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
166 		  SDHCI_QUIRK_NO_HISPD_BIT |
167 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
168 	.ops  = &tegra_sdhci_ops,
169 };
170 
171 static struct sdhci_tegra_soc_data soc_data_tegra30 = {
172 	.pdata = &sdhci_tegra30_pdata,
173 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
174 };
175 
176 static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
177 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
178 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
179 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
180 		  SDHCI_QUIRK_NO_HISPD_BIT |
181 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
182 	.ops  = &tegra_sdhci_ops,
183 };
184 
185 static struct sdhci_tegra_soc_data soc_data_tegra114 = {
186 	.pdata = &sdhci_tegra114_pdata,
187 };
188 
189 static const struct of_device_id sdhci_tegra_dt_match[] = {
190 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
191 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
192 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
193 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
194 	{}
195 };
196 MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
197 
198 static int sdhci_tegra_parse_dt(struct device *dev)
199 {
200 	struct device_node *np = dev->of_node;
201 	struct sdhci_host *host = dev_get_drvdata(dev);
202 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
203 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
204 
205 	tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
206 	return mmc_of_parse(host->mmc);
207 }
208 
209 static int sdhci_tegra_probe(struct platform_device *pdev)
210 {
211 	const struct of_device_id *match;
212 	const struct sdhci_tegra_soc_data *soc_data;
213 	struct sdhci_host *host;
214 	struct sdhci_pltfm_host *pltfm_host;
215 	struct sdhci_tegra *tegra_host;
216 	struct clk *clk;
217 	int rc;
218 
219 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
220 	if (!match)
221 		return -EINVAL;
222 	soc_data = match->data;
223 
224 	host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
225 	if (IS_ERR(host))
226 		return PTR_ERR(host);
227 	pltfm_host = sdhci_priv(host);
228 
229 	tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
230 	if (!tegra_host) {
231 		dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
232 		rc = -ENOMEM;
233 		goto err_alloc_tegra_host;
234 	}
235 	tegra_host->soc_data = soc_data;
236 	pltfm_host->priv = tegra_host;
237 
238 	rc = sdhci_tegra_parse_dt(&pdev->dev);
239 	if (rc)
240 		goto err_parse_dt;
241 
242 	if (gpio_is_valid(tegra_host->power_gpio)) {
243 		rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
244 		if (rc) {
245 			dev_err(mmc_dev(host->mmc),
246 				"failed to allocate power gpio\n");
247 			goto err_power_req;
248 		}
249 		gpio_direction_output(tegra_host->power_gpio, 1);
250 	}
251 
252 	clk = clk_get(mmc_dev(host->mmc), NULL);
253 	if (IS_ERR(clk)) {
254 		dev_err(mmc_dev(host->mmc), "clk err\n");
255 		rc = PTR_ERR(clk);
256 		goto err_clk_get;
257 	}
258 	clk_prepare_enable(clk);
259 	pltfm_host->clk = clk;
260 
261 	rc = sdhci_add_host(host);
262 	if (rc)
263 		goto err_add_host;
264 
265 	return 0;
266 
267 err_add_host:
268 	clk_disable_unprepare(pltfm_host->clk);
269 	clk_put(pltfm_host->clk);
270 err_clk_get:
271 	if (gpio_is_valid(tegra_host->power_gpio))
272 		gpio_free(tegra_host->power_gpio);
273 err_power_req:
274 err_parse_dt:
275 err_alloc_tegra_host:
276 	sdhci_pltfm_free(pdev);
277 	return rc;
278 }
279 
280 static int sdhci_tegra_remove(struct platform_device *pdev)
281 {
282 	struct sdhci_host *host = platform_get_drvdata(pdev);
283 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
284 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
285 	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
286 
287 	sdhci_remove_host(host, dead);
288 
289 	if (gpio_is_valid(tegra_host->power_gpio))
290 		gpio_free(tegra_host->power_gpio);
291 
292 	clk_disable_unprepare(pltfm_host->clk);
293 	clk_put(pltfm_host->clk);
294 
295 	sdhci_pltfm_free(pdev);
296 
297 	return 0;
298 }
299 
300 static struct platform_driver sdhci_tegra_driver = {
301 	.driver		= {
302 		.name	= "sdhci-tegra",
303 		.owner	= THIS_MODULE,
304 		.of_match_table = sdhci_tegra_dt_match,
305 		.pm	= SDHCI_PLTFM_PMOPS,
306 	},
307 	.probe		= sdhci_tegra_probe,
308 	.remove		= sdhci_tegra_remove,
309 };
310 
311 module_platform_driver(sdhci_tegra_driver);
312 
313 MODULE_DESCRIPTION("SDHCI driver for Tegra");
314 MODULE_AUTHOR("Google, Inc.");
315 MODULE_LICENSE("GPL v2");
316