blob: d264391616f9310fc4433e6aca72ac65439396c9 [file] [log] [blame]
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +05301/**
2 * SDHCI Controller driver for TI's OMAP SoCs
3 *
4 * Copyright (C) 2017 Texas Instruments
5 * Author: Kishon Vijay Abraham I <kishon@ti.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 of
9 * the License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/delay.h>
21#include <linux/mmc/slot-gpio.h>
22#include <linux/module.h>
23#include <linux/of.h>
24#include <linux/of_device.h>
25#include <linux/platform_device.h>
26#include <linux/pm_runtime.h>
27#include <linux/regulator/consumer.h>
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +053028#include <linux/pinctrl/consumer.h>
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +053029#include <linux/sys_soc.h>
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053030
31#include "sdhci-pltfm.h"
32
33#define SDHCI_OMAP_CON 0x12c
34#define CON_DW8 BIT(5)
35#define CON_DMA_MASTER BIT(20)
Kishon Vijay Abraham I27ceb7e2018-02-05 18:20:16 +053036#define CON_DDR BIT(19)
Kishon Vijay Abraham I20ea26a2018-02-05 18:20:15 +053037#define CON_CLKEXTFREE BIT(16)
38#define CON_PADEN BIT(15)
Kishon Vijay Abraham Iefde12b2018-04-27 17:17:21 +053039#define CON_CTPL BIT(11)
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053040#define CON_INIT BIT(1)
41#define CON_OD BIT(0)
42
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +053043#define SDHCI_OMAP_DLL 0x0134
44#define DLL_SWT BIT(20)
45#define DLL_FORCE_SR_C_SHIFT 13
46#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
47#define DLL_FORCE_VALUE BIT(12)
48#define DLL_CALIB BIT(1)
49
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053050#define SDHCI_OMAP_CMD 0x20c
51
Kishon Vijay Abraham I20ea26a2018-02-05 18:20:15 +053052#define SDHCI_OMAP_PSTATE 0x0224
53#define PSTATE_DLEV_DAT0 BIT(20)
54#define PSTATE_DATI BIT(1)
55
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053056#define SDHCI_OMAP_HCTL 0x228
57#define HCTL_SDBP BIT(8)
58#define HCTL_SDVS_SHIFT 9
59#define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
60#define HCTL_SDVS_33 (0x7 << HCTL_SDVS_SHIFT)
61#define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT)
62#define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT)
63
64#define SDHCI_OMAP_SYSCTL 0x22c
65#define SYSCTL_CEN BIT(2)
66#define SYSCTL_CLKD_SHIFT 6
67#define SYSCTL_CLKD_MASK 0x3ff
68
69#define SDHCI_OMAP_STAT 0x230
70
71#define SDHCI_OMAP_IE 0x234
72#define INT_CC_EN BIT(0)
73
74#define SDHCI_OMAP_AC12 0x23c
75#define AC12_V1V8_SIGEN BIT(19)
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +053076#define AC12_SCLK_SEL BIT(23)
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053077
78#define SDHCI_OMAP_CAPA 0x240
79#define CAPA_VS33 BIT(24)
80#define CAPA_VS30 BIT(25)
81#define CAPA_VS18 BIT(26)
82
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +053083#define SDHCI_OMAP_CAPA2 0x0244
84#define CAPA2_TSDR50 BIT(13)
85
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053086#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
87
88#define SYSCTL_CLKD_MAX 0x3FF
89
90#define IOV_1V8 1800000 /* 180000 uV */
91#define IOV_3V0 3000000 /* 300000 uV */
92#define IOV_3V3 3300000 /* 330000 uV */
93
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +053094#define MAX_PHASE_DELAY 0x7C
95
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +053096/* sdhci-omap controller flags */
97#define SDHCI_OMAP_REQUIRE_IODELAY BIT(0)
98
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +053099struct sdhci_omap_data {
100 u32 offset;
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530101 u8 flags;
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530102};
103
104struct sdhci_omap_host {
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530105 char *version;
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530106 void __iomem *base;
107 struct device *dev;
108 struct regulator *pbias;
109 bool pbias_enabled;
110 struct sdhci_host *host;
111 u8 bus_mode;
112 u8 power_mode;
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530113 u8 timing;
114 u8 flags;
115
116 struct pinctrl *pinctrl;
117 struct pinctrl_state **pinctrl_state;
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530118};
119
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530120static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
121static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host);
122
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530123static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
124 unsigned int offset)
125{
126 return readl(host->base + offset);
127}
128
129static inline void sdhci_omap_writel(struct sdhci_omap_host *host,
130 unsigned int offset, u32 data)
131{
132 writel(data, host->base + offset);
133}
134
135static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host,
136 bool power_on, unsigned int iov)
137{
138 int ret;
139 struct device *dev = omap_host->dev;
140
141 if (IS_ERR(omap_host->pbias))
142 return 0;
143
144 if (power_on) {
145 ret = regulator_set_voltage(omap_host->pbias, iov, iov);
146 if (ret) {
147 dev_err(dev, "pbias set voltage failed\n");
148 return ret;
149 }
150
151 if (omap_host->pbias_enabled)
152 return 0;
153
154 ret = regulator_enable(omap_host->pbias);
155 if (ret) {
156 dev_err(dev, "pbias reg enable fail\n");
157 return ret;
158 }
159
160 omap_host->pbias_enabled = true;
161 } else {
162 if (!omap_host->pbias_enabled)
163 return 0;
164
165 ret = regulator_disable(omap_host->pbias);
166 if (ret) {
167 dev_err(dev, "pbias reg disable fail\n");
168 return ret;
169 }
170 omap_host->pbias_enabled = false;
171 }
172
173 return 0;
174}
175
176static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
177 unsigned int iov)
178{
179 int ret;
180 struct sdhci_host *host = omap_host->host;
181 struct mmc_host *mmc = host->mmc;
182
183 ret = sdhci_omap_set_pbias(omap_host, false, 0);
184 if (ret)
185 return ret;
186
187 if (!IS_ERR(mmc->supply.vqmmc)) {
188 ret = regulator_set_voltage(mmc->supply.vqmmc, iov, iov);
189 if (ret) {
190 dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n");
191 return ret;
192 }
193 }
194
195 ret = sdhci_omap_set_pbias(omap_host, true, iov);
196 if (ret)
197 return ret;
198
199 return 0;
200}
201
202static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
203 unsigned char signal_voltage)
204{
205 u32 reg;
206 ktime_t timeout;
207
208 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
209 reg &= ~HCTL_SDVS_MASK;
210
211 if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
212 reg |= HCTL_SDVS_33;
213 else
214 reg |= HCTL_SDVS_18;
215
216 sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
217
218 reg |= HCTL_SDBP;
219 sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
220
221 /* wait 1ms */
222 timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
223 while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP)) {
224 if (WARN_ON(ktime_after(ktime_get(), timeout)))
225 return;
226 usleep_range(5, 10);
227 }
228}
229
Kishon Vijay Abraham Iefde12b2018-04-27 17:17:21 +0530230static void sdhci_omap_enable_sdio_irq(struct mmc_host *mmc, int enable)
231{
232 struct sdhci_host *host = mmc_priv(mmc);
233 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
234 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
235 u32 reg;
236
237 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
238 if (enable)
239 reg |= (CON_CTPL | CON_CLKEXTFREE);
240 else
241 reg &= ~(CON_CTPL | CON_CLKEXTFREE);
242 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
243
244 sdhci_enable_sdio_irq(mmc, enable);
245}
246
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +0530247static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
248 int count)
249{
250 int i;
251 u32 reg;
252
253 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
254 reg |= DLL_FORCE_VALUE;
255 reg &= ~DLL_FORCE_SR_C_MASK;
256 reg |= (count << DLL_FORCE_SR_C_SHIFT);
257 sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
258
259 reg |= DLL_CALIB;
260 sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
261 for (i = 0; i < 1000; i++) {
262 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
263 if (reg & DLL_CALIB)
264 break;
265 }
266 reg &= ~DLL_CALIB;
267 sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
268}
269
270static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host)
271{
272 u32 reg;
273
274 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
275 reg &= ~AC12_SCLK_SEL;
276 sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
277
278 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
279 reg &= ~(DLL_FORCE_VALUE | DLL_SWT);
280 sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
281}
282
283static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
284{
285 struct sdhci_host *host = mmc_priv(mmc);
286 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
287 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
288 struct device *dev = omap_host->dev;
289 struct mmc_ios *ios = &mmc->ios;
290 u32 start_window = 0, max_window = 0;
Faiz Abbasdb2039f2018-11-21 16:03:55 +0530291 bool dcrc_was_enabled = false;
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +0530292 u8 cur_match, prev_match = 0;
293 u32 length = 0, max_len = 0;
294 u32 phase_delay = 0;
295 int ret = 0;
296 u32 reg;
297
298 pltfm_host = sdhci_priv(host);
299 omap_host = sdhci_pltfm_priv(pltfm_host);
300 dev = omap_host->dev;
301
302 /* clock tuning is not needed for upto 52MHz */
303 if (ios->clock <= 52000000)
304 return 0;
305
306 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2);
307 if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
308 return 0;
309
310 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
311 reg |= DLL_SWT;
312 sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
313
Kishon Vijay Abraham I7d33c352018-02-05 18:20:18 +0530314 /*
315 * OMAP5/DRA74X/DRA72x Errata i802:
316 * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
317 * during the tuning procedure. So disable it during the
318 * tuning procedure.
319 */
Faiz Abbasdb2039f2018-11-21 16:03:55 +0530320 if (host->ier & SDHCI_INT_DATA_CRC) {
321 host->ier &= ~SDHCI_INT_DATA_CRC;
322 dcrc_was_enabled = true;
323 }
Kishon Vijay Abraham I7d33c352018-02-05 18:20:18 +0530324
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +0530325 while (phase_delay <= MAX_PHASE_DELAY) {
326 sdhci_omap_set_dll(omap_host, phase_delay);
327
328 cur_match = !mmc_send_tuning(mmc, opcode, NULL);
329 if (cur_match) {
330 if (prev_match) {
331 length++;
332 } else {
333 start_window = phase_delay;
334 length = 1;
335 }
336 }
337
338 if (length > max_len) {
339 max_window = start_window;
340 max_len = length;
341 }
342
343 prev_match = cur_match;
344 phase_delay += 4;
345 }
346
347 if (!max_len) {
348 dev_err(dev, "Unable to find match\n");
349 ret = -EIO;
350 goto tuning_error;
351 }
352
353 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
354 if (!(reg & AC12_SCLK_SEL)) {
355 ret = -EIO;
356 goto tuning_error;
357 }
358
359 phase_delay = max_window + 4 * (max_len >> 1);
360 sdhci_omap_set_dll(omap_host, phase_delay);
361
362 goto ret;
363
364tuning_error:
365 dev_err(dev, "Tuning failed\n");
366 sdhci_omap_disable_tuning(omap_host);
367
368ret:
369 sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
Faiz Abbasdb2039f2018-11-21 16:03:55 +0530370 /* Reenable forbidden interrupt */
371 if (dcrc_was_enabled)
372 host->ier |= SDHCI_INT_DATA_CRC;
Kishon Vijay Abraham I7d33c352018-02-05 18:20:18 +0530373 sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
374 sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +0530375 return ret;
376}
377
Kishon Vijay Abraham I20ea26a2018-02-05 18:20:15 +0530378static int sdhci_omap_card_busy(struct mmc_host *mmc)
379{
380 u32 reg, ac12;
381 int ret = false;
382 struct sdhci_host *host = mmc_priv(mmc);
383 struct sdhci_pltfm_host *pltfm_host;
384 struct sdhci_omap_host *omap_host;
385 u32 ier = host->ier;
386
387 pltfm_host = sdhci_priv(host);
388 omap_host = sdhci_pltfm_priv(pltfm_host);
389
390 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
391 ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
392 reg &= ~CON_CLKEXTFREE;
393 if (ac12 & AC12_V1V8_SIGEN)
394 reg |= CON_CLKEXTFREE;
395 reg |= CON_PADEN;
396 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
397
398 disable_irq(host->irq);
399 ier |= SDHCI_INT_CARD_INT;
400 sdhci_writel(host, ier, SDHCI_INT_ENABLE);
401 sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
402
403 /*
404 * Delay is required for PSTATE to correctly reflect
405 * DLEV/CLEV values after PADEN is set.
406 */
407 usleep_range(50, 100);
408 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE);
409 if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
410 ret = true;
411
412 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
413 reg &= ~(CON_CLKEXTFREE | CON_PADEN);
414 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
415
416 sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
417 sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
418 enable_irq(host->irq);
419
420 return ret;
421}
422
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530423static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
424 struct mmc_ios *ios)
425{
426 u32 reg;
427 int ret;
428 unsigned int iov;
429 struct sdhci_host *host = mmc_priv(mmc);
430 struct sdhci_pltfm_host *pltfm_host;
431 struct sdhci_omap_host *omap_host;
432 struct device *dev;
433
434 pltfm_host = sdhci_priv(host);
435 omap_host = sdhci_pltfm_priv(pltfm_host);
436 dev = omap_host->dev;
437
438 if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
439 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
440 if (!(reg & CAPA_VS33))
441 return -EOPNOTSUPP;
442
443 sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
444
445 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
446 reg &= ~AC12_V1V8_SIGEN;
447 sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
448
449 iov = IOV_3V3;
450 } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
451 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
452 if (!(reg & CAPA_VS18))
453 return -EOPNOTSUPP;
454
455 sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
456
457 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
458 reg |= AC12_V1V8_SIGEN;
459 sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
460
461 iov = IOV_1V8;
462 } else {
463 return -EOPNOTSUPP;
464 }
465
466 ret = sdhci_omap_enable_iov(omap_host, iov);
467 if (ret) {
468 dev_err(dev, "failed to switch IO voltage to %dmV\n", iov);
469 return ret;
470 }
471
472 dev_dbg(dev, "IO voltage switched to %dmV\n", iov);
473 return 0;
474}
475
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530476static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing)
477{
478 int ret;
479 struct pinctrl_state *pinctrl_state;
480 struct device *dev = omap_host->dev;
481
482 if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
483 return;
484
485 if (omap_host->timing == timing)
486 return;
487
488 sdhci_omap_stop_clock(omap_host);
489
490 pinctrl_state = omap_host->pinctrl_state[timing];
491 ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state);
492 if (ret) {
493 dev_err(dev, "failed to select pinctrl state\n");
494 return;
495 }
496
497 sdhci_omap_start_clock(omap_host);
498 omap_host->timing = timing;
499}
500
Kishon Vijay Abraham I300df502018-02-05 18:20:14 +0530501static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host,
502 u8 power_mode)
503{
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +0530504 if (omap_host->bus_mode == MMC_POWER_OFF)
505 sdhci_omap_disable_tuning(omap_host);
Kishon Vijay Abraham I300df502018-02-05 18:20:14 +0530506 omap_host->power_mode = power_mode;
507}
508
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530509static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
510 unsigned int mode)
511{
512 u32 reg;
513
514 if (omap_host->bus_mode == mode)
515 return;
516
517 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
518 if (mode == MMC_BUSMODE_OPENDRAIN)
519 reg |= CON_OD;
520 else
521 reg &= ~CON_OD;
522 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
523
524 omap_host->bus_mode = mode;
525}
526
Colin Ian Kingddde0e72017-09-26 15:55:46 +0100527static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530528{
529 struct sdhci_host *host = mmc_priv(mmc);
530 struct sdhci_pltfm_host *pltfm_host;
531 struct sdhci_omap_host *omap_host;
532
533 pltfm_host = sdhci_priv(host);
534 omap_host = sdhci_pltfm_priv(pltfm_host);
535
536 sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530537 sdhci_omap_set_timing(omap_host, ios->timing);
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530538 sdhci_set_ios(mmc, ios);
Kishon Vijay Abraham I300df502018-02-05 18:20:14 +0530539 sdhci_omap_set_power_mode(omap_host, ios->power_mode);
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530540}
541
542static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
543 unsigned int clock)
544{
545 u16 dsor;
546
547 dsor = DIV_ROUND_UP(clk_get_rate(host->clk), clock);
548 if (dsor > SYSCTL_CLKD_MAX)
549 dsor = SYSCTL_CLKD_MAX;
550
551 return dsor;
552}
553
554static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host)
555{
556 u32 reg;
557
558 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
559 reg |= SYSCTL_CEN;
560 sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg);
561}
562
563static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host)
564{
565 u32 reg;
566
567 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
568 reg &= ~SYSCTL_CEN;
569 sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg);
570}
571
572static void sdhci_omap_set_clock(struct sdhci_host *host, unsigned int clock)
573{
574 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
575 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
576 unsigned long clkdiv;
577
578 sdhci_omap_stop_clock(omap_host);
579
580 if (!clock)
581 return;
582
583 clkdiv = sdhci_omap_calc_divisor(pltfm_host, clock);
584 clkdiv = (clkdiv & SYSCTL_CLKD_MASK) << SYSCTL_CLKD_SHIFT;
585 sdhci_enable_clk(host, clkdiv);
586
587 sdhci_omap_start_clock(omap_host);
588}
589
Colin Ian Kingddde0e72017-09-26 15:55:46 +0100590static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode,
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530591 unsigned short vdd)
592{
593 struct mmc_host *mmc = host->mmc;
594
595 mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
596}
597
598static int sdhci_omap_enable_dma(struct sdhci_host *host)
599{
600 u32 reg;
601 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
602 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
603
604 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
605 reg |= CON_DMA_MASTER;
606 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
607
608 return 0;
609}
610
Colin Ian Kingddde0e72017-09-26 15:55:46 +0100611static unsigned int sdhci_omap_get_min_clock(struct sdhci_host *host)
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530612{
613 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
614
615 return clk_get_rate(pltfm_host->clk) / SYSCTL_CLKD_MAX;
616}
617
618static void sdhci_omap_set_bus_width(struct sdhci_host *host, int width)
619{
620 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
621 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
622 u32 reg;
623
624 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
625 if (width == MMC_BUS_WIDTH_8)
626 reg |= CON_DW8;
627 else
628 reg &= ~CON_DW8;
629 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
630
631 sdhci_set_bus_width(host, width);
632}
633
634static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
635{
636 u32 reg;
637 ktime_t timeout;
638 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
639 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
640
641 if (omap_host->power_mode == power_mode)
642 return;
643
644 if (power_mode != MMC_POWER_ON)
645 return;
646
647 disable_irq(host->irq);
648
649 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
650 reg |= CON_INIT;
651 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
652 sdhci_omap_writel(omap_host, SDHCI_OMAP_CMD, 0x0);
653
654 /* wait 1ms */
655 timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
656 while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN)) {
657 if (WARN_ON(ktime_after(ktime_get(), timeout)))
658 return;
659 usleep_range(5, 10);
660 }
661
662 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
663 reg &= ~CON_INIT;
664 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
665 sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
666
667 enable_irq(host->irq);
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530668}
669
Kishon Vijay Abraham I27ceb7e2018-02-05 18:20:16 +0530670static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
671 unsigned int timing)
672{
673 u32 reg;
674 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
675 struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
676
677 sdhci_omap_stop_clock(omap_host);
678
679 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
680 if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
681 reg |= CON_DDR;
682 else
683 reg &= ~CON_DDR;
684 sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
685
686 sdhci_set_uhs_signaling(host, timing);
687 sdhci_omap_start_clock(omap_host);
688}
689
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530690static struct sdhci_ops sdhci_omap_ops = {
691 .set_clock = sdhci_omap_set_clock,
692 .set_power = sdhci_omap_set_power,
693 .enable_dma = sdhci_omap_enable_dma,
694 .get_max_clock = sdhci_pltfm_clk_get_max_clock,
695 .get_min_clock = sdhci_omap_get_min_clock,
696 .set_bus_width = sdhci_omap_set_bus_width,
697 .platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
698 .reset = sdhci_reset,
Kishon Vijay Abraham I27ceb7e2018-02-05 18:20:16 +0530699 .set_uhs_signaling = sdhci_omap_set_uhs_signaling,
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530700};
701
702static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
703{
704 u32 reg;
705 int ret = 0;
706 struct device *dev = omap_host->dev;
707 struct regulator *vqmmc;
708
709 vqmmc = regulator_get(dev, "vqmmc");
710 if (IS_ERR(vqmmc)) {
711 ret = PTR_ERR(vqmmc);
712 goto reg_put;
713 }
714
715 /* voltage capabilities might be set by boot loader, clear it */
716 reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
717 reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
718
719 if (regulator_is_supported_voltage(vqmmc, IOV_3V3, IOV_3V3))
720 reg |= CAPA_VS33;
721 if (regulator_is_supported_voltage(vqmmc, IOV_1V8, IOV_1V8))
722 reg |= CAPA_VS18;
723
724 sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg);
725
726reg_put:
727 regulator_put(vqmmc);
728
729 return ret;
730}
731
732static const struct sdhci_pltfm_data sdhci_omap_pdata = {
733 .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
734 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
735 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
736 SDHCI_QUIRK_NO_HISPD_BIT |
737 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
Kishon Vijay Abraham Ie0b2dbc2018-02-05 18:20:20 +0530738 .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
739 SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
Kishon Vijay Abraham I25f80d82018-04-27 17:17:18 +0530740 SDHCI_QUIRK2_RSP_136_HAS_CRC |
741 SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530742 .ops = &sdhci_omap_ops,
743};
744
Kishon Vijay Abraham I6d75df72018-04-27 17:17:20 +0530745static const struct sdhci_omap_data k2g_data = {
746 .offset = 0x200,
747};
748
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530749static const struct sdhci_omap_data dra7_data = {
750 .offset = 0x200,
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530751 .flags = SDHCI_OMAP_REQUIRE_IODELAY,
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530752};
753
754static const struct of_device_id omap_sdhci_match[] = {
755 { .compatible = "ti,dra7-sdhci", .data = &dra7_data },
Kishon Vijay Abraham I6d75df72018-04-27 17:17:20 +0530756 { .compatible = "ti,k2g-sdhci", .data = &k2g_data },
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530757 {},
758};
759MODULE_DEVICE_TABLE(of, omap_sdhci_match);
760
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530761static struct pinctrl_state
762*sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode,
763 u32 *caps, u32 capmask)
764{
765 struct device *dev = omap_host->dev;
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530766 char *version = omap_host->version;
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530767 struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530768 char str[20];
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530769
770 if (!(*caps & capmask))
771 goto ret;
772
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530773 if (version) {
774 snprintf(str, 20, "%s-%s", mode, version);
775 pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, str);
776 }
777
778 if (IS_ERR(pinctrl_state))
779 pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
780
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530781 if (IS_ERR(pinctrl_state)) {
782 dev_err(dev, "no pinctrl state for %s mode", mode);
783 *caps &= ~capmask;
784 }
785
786ret:
787 return pinctrl_state;
788}
789
790static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
791 *omap_host)
792{
793 struct device *dev = omap_host->dev;
794 struct sdhci_host *host = omap_host->host;
795 struct mmc_host *mmc = host->mmc;
796 u32 *caps = &mmc->caps;
797 u32 *caps2 = &mmc->caps2;
798 struct pinctrl_state *state;
799 struct pinctrl_state **pinctrl_state;
800
801 if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
802 return 0;
803
Kees Cooka86854d2018-06-12 14:07:58 -0700804 pinctrl_state = devm_kcalloc(dev,
805 MMC_TIMING_MMC_HS200 + 1,
806 sizeof(*pinctrl_state),
807 GFP_KERNEL);
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530808 if (!pinctrl_state)
809 return -ENOMEM;
810
811 omap_host->pinctrl = devm_pinctrl_get(omap_host->dev);
812 if (IS_ERR(omap_host->pinctrl)) {
813 dev_err(dev, "Cannot get pinctrl\n");
814 return PTR_ERR(omap_host->pinctrl);
815 }
816
817 state = pinctrl_lookup_state(omap_host->pinctrl, "default");
818 if (IS_ERR(state)) {
819 dev_err(dev, "no pinctrl state for default mode\n");
820 return PTR_ERR(state);
821 }
822 pinctrl_state[MMC_TIMING_LEGACY] = state;
823
824 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps,
825 MMC_CAP_UHS_SDR104);
826 if (!IS_ERR(state))
827 pinctrl_state[MMC_TIMING_UHS_SDR104] = state;
828
829 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps,
830 MMC_CAP_UHS_DDR50);
831 if (!IS_ERR(state))
832 pinctrl_state[MMC_TIMING_UHS_DDR50] = state;
833
834 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps,
835 MMC_CAP_UHS_SDR50);
836 if (!IS_ERR(state))
837 pinctrl_state[MMC_TIMING_UHS_SDR50] = state;
838
839 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps,
840 MMC_CAP_UHS_SDR25);
841 if (!IS_ERR(state))
842 pinctrl_state[MMC_TIMING_UHS_SDR25] = state;
843
844 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps,
845 MMC_CAP_UHS_SDR12);
846 if (!IS_ERR(state))
847 pinctrl_state[MMC_TIMING_UHS_SDR12] = state;
848
849 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
850 MMC_CAP_1_8V_DDR);
Kishon Vijay Abraham I3f402872018-04-27 17:17:23 +0530851 if (!IS_ERR(state)) {
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530852 pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
Kishon Vijay Abraham I3f402872018-04-27 17:17:23 +0530853 } else {
854 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_3_3v",
855 caps,
856 MMC_CAP_3_3V_DDR);
857 if (!IS_ERR(state))
858 pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
859 }
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530860
861 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
862 MMC_CAP_SD_HIGHSPEED);
863 if (!IS_ERR(state))
864 pinctrl_state[MMC_TIMING_SD_HS] = state;
865
866 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
867 MMC_CAP_MMC_HIGHSPEED);
868 if (!IS_ERR(state))
869 pinctrl_state[MMC_TIMING_MMC_HS] = state;
870
871 state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2,
872 MMC_CAP2_HS200_1_8V_SDR);
873 if (!IS_ERR(state))
874 pinctrl_state[MMC_TIMING_MMC_HS200] = state;
875
876 omap_host->pinctrl_state = pinctrl_state;
877
878 return 0;
879}
880
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530881static const struct soc_device_attribute sdhci_omap_soc_devices[] = {
882 {
883 .machine = "DRA7[45]*",
884 .revision = "ES1.[01]",
885 },
886 {
887 /* sentinel */
888 }
889};
890
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530891static int sdhci_omap_probe(struct platform_device *pdev)
892{
893 int ret;
894 u32 offset;
895 struct device *dev = &pdev->dev;
896 struct sdhci_host *host;
897 struct sdhci_pltfm_host *pltfm_host;
898 struct sdhci_omap_host *omap_host;
899 struct mmc_host *mmc;
900 const struct of_device_id *match;
901 struct sdhci_omap_data *data;
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530902 const struct soc_device_attribute *soc;
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530903
904 match = of_match_device(omap_sdhci_match, dev);
905 if (!match)
906 return -EINVAL;
907
908 data = (struct sdhci_omap_data *)match->data;
909 if (!data) {
910 dev_err(dev, "no sdhci omap data\n");
911 return -EINVAL;
912 }
913 offset = data->offset;
914
915 host = sdhci_pltfm_init(pdev, &sdhci_omap_pdata,
916 sizeof(*omap_host));
917 if (IS_ERR(host)) {
918 dev_err(dev, "Failed sdhci_pltfm_init\n");
919 return PTR_ERR(host);
920 }
921
922 pltfm_host = sdhci_priv(host);
923 omap_host = sdhci_pltfm_priv(pltfm_host);
924 omap_host->host = host;
925 omap_host->base = host->ioaddr;
926 omap_host->dev = dev;
Kishon Vijay Abraham I300df502018-02-05 18:20:14 +0530927 omap_host->power_mode = MMC_POWER_UNDEFINED;
Kishon Vijay Abraham I8d20b2e2018-02-05 18:20:19 +0530928 omap_host->timing = MMC_TIMING_LEGACY;
929 omap_host->flags = data->flags;
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530930 host->ioaddr += offset;
931
932 mmc = host->mmc;
Kishon Vijay Abraham I1d3a2222018-04-27 17:17:13 +0530933 sdhci_get_of_property(pdev);
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530934 ret = mmc_of_parse(mmc);
935 if (ret)
936 goto err_pltfm_free;
937
Kishon Vijay Abraham I212f4f82018-04-27 17:17:12 +0530938 soc = soc_device_match(sdhci_omap_soc_devices);
939 if (soc) {
940 omap_host->version = "rev11";
941 if (!strcmp(dev_name(dev), "4809c000.mmc"))
942 mmc->f_max = 96000000;
943 if (!strcmp(dev_name(dev), "480b4000.mmc"))
944 mmc->f_max = 48000000;
945 if (!strcmp(dev_name(dev), "480ad000.mmc"))
946 mmc->f_max = 48000000;
947 }
948
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530949 pltfm_host->clk = devm_clk_get(dev, "fck");
950 if (IS_ERR(pltfm_host->clk)) {
951 ret = PTR_ERR(pltfm_host->clk);
952 goto err_pltfm_free;
953 }
954
955 ret = clk_set_rate(pltfm_host->clk, mmc->f_max);
956 if (ret) {
957 dev_err(dev, "failed to set clock to %d\n", mmc->f_max);
958 goto err_pltfm_free;
959 }
960
961 omap_host->pbias = devm_regulator_get_optional(dev, "pbias");
962 if (IS_ERR(omap_host->pbias)) {
963 ret = PTR_ERR(omap_host->pbias);
964 if (ret != -ENODEV)
965 goto err_pltfm_free;
966 dev_dbg(dev, "unable to get pbias regulator %d\n", ret);
967 }
968 omap_host->pbias_enabled = false;
969
970 /*
971 * omap_device_pm_domain has callbacks to enable the main
972 * functional clock, interface clock and also configure the
973 * SYSCONFIG register of omap devices. The callback will be invoked
974 * as part of pm_runtime_get_sync.
975 */
976 pm_runtime_enable(dev);
977 ret = pm_runtime_get_sync(dev);
978 if (ret < 0) {
979 dev_err(dev, "pm_runtime_get_sync failed\n");
980 pm_runtime_put_noidle(dev);
981 goto err_rpm_disable;
982 }
983
984 ret = sdhci_omap_set_capabilities(omap_host);
985 if (ret) {
986 dev_err(dev, "failed to set system capabilities\n");
987 goto err_put_sync;
988 }
989
990 host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
991 host->mmc_host_ops.start_signal_voltage_switch =
992 sdhci_omap_start_signal_voltage_switch;
993 host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
Kishon Vijay Abraham I20ea26a2018-02-05 18:20:15 +0530994 host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
Kishon Vijay Abraham I9fc2cd72018-02-05 18:20:17 +0530995 host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
Kishon Vijay Abraham Iefde12b2018-04-27 17:17:21 +0530996 host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530997
Kishon Vijay Abraham I0ec4ee32018-04-27 17:17:10 +0530998 ret = sdhci_setup_host(host);
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +0530999 if (ret)
1000 goto err_put_sync;
1001
Kishon Vijay Abraham I0ec4ee32018-04-27 17:17:10 +05301002 ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
1003 if (ret)
1004 goto err_cleanup_host;
1005
1006 ret = __sdhci_add_host(host);
1007 if (ret)
1008 goto err_cleanup_host;
1009
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +05301010 return 0;
1011
Kishon Vijay Abraham I0ec4ee32018-04-27 17:17:10 +05301012err_cleanup_host:
1013 sdhci_cleanup_host(host);
1014
Kishon Vijay Abraham I7d326932017-09-06 17:15:55 +05301015err_put_sync:
1016 pm_runtime_put_sync(dev);
1017
1018err_rpm_disable:
1019 pm_runtime_disable(dev);
1020
1021err_pltfm_free:
1022 sdhci_pltfm_free(pdev);
1023 return ret;
1024}
1025
1026static int sdhci_omap_remove(struct platform_device *pdev)
1027{
1028 struct device *dev = &pdev->dev;
1029 struct sdhci_host *host = platform_get_drvdata(pdev);
1030
1031 sdhci_remove_host(host, true);
1032 pm_runtime_put_sync(dev);
1033 pm_runtime_disable(dev);
1034 sdhci_pltfm_free(pdev);
1035
1036 return 0;
1037}
1038
1039static struct platform_driver sdhci_omap_driver = {
1040 .probe = sdhci_omap_probe,
1041 .remove = sdhci_omap_remove,
1042 .driver = {
1043 .name = "sdhci-omap",
1044 .of_match_table = omap_sdhci_match,
1045 },
1046};
1047
1048module_platform_driver(sdhci_omap_driver);
1049
1050MODULE_DESCRIPTION("SDHCI driver for OMAP SoCs");
1051MODULE_AUTHOR("Texas Instruments Inc.");
1052MODULE_LICENSE("GPL v2");
1053MODULE_ALIAS("platform:sdhci_omap");