]>
Commit | Line | Data |
---|---|---|
21ef6a10 TW |
1 | /* |
2 | * (C) Copyright 2009 SAMSUNG Electronics | |
3 | * Minkyu Kang <mk7.kang@samsung.com> | |
4 | * Jaehoon Chung <jh80.chung@samsung.com> | |
6a474db4 | 5 | * Portions Copyright 2011-2016 NVIDIA Corporation |
21ef6a10 | 6 | * |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
21ef6a10 TW |
8 | */ |
9 | ||
19815399 | 10 | #include <bouncebuf.h> |
21ef6a10 | 11 | #include <common.h> |
9d922450 | 12 | #include <dm.h> |
915ffa52 | 13 | #include <errno.h> |
9877841f | 14 | #include <asm/gpio.h> |
21ef6a10 | 15 | #include <asm/io.h> |
150c2493 TW |
16 | #include <asm/arch-tegra/tegra_mmc.h> |
17 | #include <mmc.h> | |
21ef6a10 | 18 | |
c9aa831e | 19 | DECLARE_GLOBAL_DATA_PTR; |
21ef6a10 | 20 | |
f53c4e4b SW |
21 | struct tegra_mmc_priv { |
22 | struct tegra_mmc *reg; | |
f53c4e4b SW |
23 | struct reset_ctl reset_ctl; |
24 | struct clk clk; | |
f53c4e4b SW |
25 | struct gpio_desc cd_gpio; /* Change Detect GPIO */ |
26 | struct gpio_desc pwr_gpio; /* Power GPIO */ | |
27 | struct gpio_desc wp_gpio; /* Write Protect GPIO */ | |
28 | unsigned int version; /* SDHCI spec. version */ | |
29 | unsigned int clock; /* Current clock (MHz) */ | |
30 | struct mmc_config cfg; /* mmc configuration */ | |
6a474db4 | 31 | struct mmc *mmc; |
f53c4e4b SW |
32 | }; |
33 | ||
f53c4e4b SW |
34 | static void tegra_mmc_set_power(struct tegra_mmc_priv *priv, |
35 | unsigned short power) | |
2d348a16 TW |
36 | { |
37 | u8 pwr = 0; | |
38 | debug("%s: power = %x\n", __func__, power); | |
39 | ||
40 | if (power != (unsigned short)-1) { | |
41 | switch (1 << power) { | |
42 | case MMC_VDD_165_195: | |
43 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; | |
44 | break; | |
45 | case MMC_VDD_29_30: | |
46 | case MMC_VDD_30_31: | |
47 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; | |
48 | break; | |
49 | case MMC_VDD_32_33: | |
50 | case MMC_VDD_33_34: | |
51 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; | |
52 | break; | |
53 | } | |
54 | } | |
55 | debug("%s: pwr = %X\n", __func__, pwr); | |
56 | ||
57 | /* Set the bus voltage first (if any) */ | |
f53c4e4b | 58 | writeb(pwr, &priv->reg->pwrcon); |
2d348a16 TW |
59 | if (pwr == 0) |
60 | return; | |
61 | ||
62 | /* Now enable bus power */ | |
63 | pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; | |
f53c4e4b | 64 | writeb(pwr, &priv->reg->pwrcon); |
2d348a16 TW |
65 | } |
66 | ||
f53c4e4b SW |
67 | static void tegra_mmc_prepare_data(struct tegra_mmc_priv *priv, |
68 | struct mmc_data *data, | |
69 | struct bounce_buffer *bbstate) | |
21ef6a10 TW |
70 | { |
71 | unsigned char ctrl; | |
72 | ||
21ef6a10 | 73 | |
19815399 SW |
74 | debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", |
75 | bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, | |
76 | data->blocksize); | |
77 | ||
f53c4e4b | 78 | writel((u32)(unsigned long)bbstate->bounce_buffer, &priv->reg->sysad); |
21ef6a10 TW |
79 | /* |
80 | * DMASEL[4:3] | |
81 | * 00 = Selects SDMA | |
82 | * 01 = Reserved | |
83 | * 10 = Selects 32-bit Address ADMA2 | |
84 | * 11 = Selects 64-bit Address ADMA2 | |
85 | */ | |
f53c4e4b | 86 | ctrl = readb(&priv->reg->hostctl); |
8e42f0d6 A |
87 | ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; |
88 | ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; | |
f53c4e4b | 89 | writeb(ctrl, &priv->reg->hostctl); |
21ef6a10 TW |
90 | |
91 | /* We do not handle DMA boundaries, so set it to max (512 KiB) */ | |
f53c4e4b SW |
92 | writew((7 << 12) | (data->blocksize & 0xFFF), &priv->reg->blksize); |
93 | writew(data->blocks, &priv->reg->blkcnt); | |
21ef6a10 TW |
94 | } |
95 | ||
f53c4e4b SW |
96 | static void tegra_mmc_set_transfer_mode(struct tegra_mmc_priv *priv, |
97 | struct mmc_data *data) | |
21ef6a10 TW |
98 | { |
99 | unsigned short mode; | |
100 | debug(" mmc_set_transfer_mode called\n"); | |
101 | /* | |
102 | * TRNMOD | |
103 | * MUL1SIN0[5] : Multi/Single Block Select | |
104 | * RD1WT0[4] : Data Transfer Direction Select | |
105 | * 1 = read | |
106 | * 0 = write | |
107 | * ENACMD12[2] : Auto CMD12 Enable | |
108 | * ENBLKCNT[1] : Block Count Enable | |
109 | * ENDMA[0] : DMA Enable | |
110 | */ | |
8e42f0d6 A |
111 | mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | |
112 | TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); | |
113 | ||
21ef6a10 | 114 | if (data->blocks > 1) |
8e42f0d6 A |
115 | mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; |
116 | ||
21ef6a10 | 117 | if (data->flags & MMC_DATA_READ) |
8e42f0d6 | 118 | mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; |
21ef6a10 | 119 | |
f53c4e4b | 120 | writew(mode, &priv->reg->trnmod); |
21ef6a10 TW |
121 | } |
122 | ||
f53c4e4b SW |
123 | static int tegra_mmc_wait_inhibit(struct tegra_mmc_priv *priv, |
124 | struct mmc_cmd *cmd, | |
125 | struct mmc_data *data, | |
126 | unsigned int timeout) | |
21ef6a10 | 127 | { |
21ef6a10 TW |
128 | /* |
129 | * PRNSTS | |
0963ff3a A |
130 | * CMDINHDAT[1] : Command Inhibit (DAT) |
131 | * CMDINHCMD[0] : Command Inhibit (CMD) | |
21ef6a10 | 132 | */ |
0963ff3a | 133 | unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; |
21ef6a10 TW |
134 | |
135 | /* | |
136 | * We shouldn't wait for data inhibit for stop commands, even | |
137 | * though they might use busy signaling | |
138 | */ | |
0963ff3a A |
139 | if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) |
140 | mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; | |
21ef6a10 | 141 | |
f53c4e4b | 142 | while (readl(&priv->reg->prnsts) & mask) { |
21ef6a10 TW |
143 | if (timeout == 0) { |
144 | printf("%s: timeout error\n", __func__); | |
145 | return -1; | |
146 | } | |
147 | timeout--; | |
148 | udelay(1000); | |
149 | } | |
150 | ||
0963ff3a A |
151 | return 0; |
152 | } | |
153 | ||
f53c4e4b SW |
154 | static int tegra_mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, |
155 | struct mmc_data *data, | |
156 | struct bounce_buffer *bbstate) | |
0963ff3a | 157 | { |
f53c4e4b | 158 | struct tegra_mmc_priv *priv = mmc->priv; |
0963ff3a A |
159 | int flags, i; |
160 | int result; | |
60e242ed | 161 | unsigned int mask = 0; |
0963ff3a A |
162 | unsigned int retry = 0x100000; |
163 | debug(" mmc_send_cmd called\n"); | |
164 | ||
f53c4e4b | 165 | result = tegra_mmc_wait_inhibit(priv, cmd, data, 10 /* ms */); |
0963ff3a A |
166 | |
167 | if (result < 0) | |
168 | return result; | |
169 | ||
21ef6a10 | 170 | if (data) |
f53c4e4b | 171 | tegra_mmc_prepare_data(priv, data, bbstate); |
21ef6a10 TW |
172 | |
173 | debug("cmd->arg: %08x\n", cmd->cmdarg); | |
f53c4e4b | 174 | writel(cmd->cmdarg, &priv->reg->argument); |
21ef6a10 TW |
175 | |
176 | if (data) | |
f53c4e4b | 177 | tegra_mmc_set_transfer_mode(priv, data); |
21ef6a10 TW |
178 | |
179 | if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) | |
180 | return -1; | |
181 | ||
182 | /* | |
183 | * CMDREG | |
184 | * CMDIDX[13:8] : Command index | |
185 | * DATAPRNT[5] : Data Present Select | |
186 | * ENCMDIDX[4] : Command Index Check Enable | |
187 | * ENCMDCRC[3] : Command CRC Check Enable | |
188 | * RSPTYP[1:0] | |
189 | * 00 = No Response | |
190 | * 01 = Length 136 | |
191 | * 10 = Length 48 | |
192 | * 11 = Length 48 Check busy after response | |
193 | */ | |
194 | if (!(cmd->resp_type & MMC_RSP_PRESENT)) | |
8e42f0d6 | 195 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; |
21ef6a10 | 196 | else if (cmd->resp_type & MMC_RSP_136) |
8e42f0d6 | 197 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; |
21ef6a10 | 198 | else if (cmd->resp_type & MMC_RSP_BUSY) |
8e42f0d6 | 199 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; |
21ef6a10 | 200 | else |
8e42f0d6 | 201 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; |
21ef6a10 TW |
202 | |
203 | if (cmd->resp_type & MMC_RSP_CRC) | |
8e42f0d6 | 204 | flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; |
21ef6a10 | 205 | if (cmd->resp_type & MMC_RSP_OPCODE) |
8e42f0d6 | 206 | flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; |
21ef6a10 | 207 | if (data) |
8e42f0d6 | 208 | flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; |
21ef6a10 TW |
209 | |
210 | debug("cmd: %d\n", cmd->cmdidx); | |
211 | ||
f53c4e4b | 212 | writew((cmd->cmdidx << 8) | flags, &priv->reg->cmdreg); |
21ef6a10 TW |
213 | |
214 | for (i = 0; i < retry; i++) { | |
f53c4e4b | 215 | mask = readl(&priv->reg->norintsts); |
21ef6a10 | 216 | /* Command Complete */ |
8e42f0d6 | 217 | if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { |
21ef6a10 | 218 | if (!data) |
f53c4e4b | 219 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
220 | break; |
221 | } | |
222 | } | |
223 | ||
224 | if (i == retry) { | |
225 | printf("%s: waiting for status update\n", __func__); | |
f53c4e4b | 226 | writel(mask, &priv->reg->norintsts); |
915ffa52 | 227 | return -ETIMEDOUT; |
21ef6a10 TW |
228 | } |
229 | ||
8e42f0d6 | 230 | if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { |
21ef6a10 TW |
231 | /* Timeout Error */ |
232 | debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); | |
f53c4e4b | 233 | writel(mask, &priv->reg->norintsts); |
915ffa52 | 234 | return -ETIMEDOUT; |
8e42f0d6 | 235 | } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 TW |
236 | /* Error Interrupt */ |
237 | debug("error: %08x cmd %d\n", mask, cmd->cmdidx); | |
f53c4e4b | 238 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
239 | return -1; |
240 | } | |
241 | ||
242 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
243 | if (cmd->resp_type & MMC_RSP_136) { | |
244 | /* CRC is stripped so we need to do some shifting. */ | |
245 | for (i = 0; i < 4; i++) { | |
f53c4e4b SW |
246 | unsigned long offset = (unsigned long) |
247 | (&priv->reg->rspreg3 - i); | |
21ef6a10 TW |
248 | cmd->response[i] = readl(offset) << 8; |
249 | ||
250 | if (i != 3) { | |
251 | cmd->response[i] |= | |
252 | readb(offset - 1); | |
253 | } | |
254 | debug("cmd->resp[%d]: %08x\n", | |
255 | i, cmd->response[i]); | |
256 | } | |
257 | } else if (cmd->resp_type & MMC_RSP_BUSY) { | |
258 | for (i = 0; i < retry; i++) { | |
259 | /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ | |
f53c4e4b | 260 | if (readl(&priv->reg->prnsts) |
21ef6a10 TW |
261 | & (1 << 20)) /* DAT[0] */ |
262 | break; | |
263 | } | |
264 | ||
265 | if (i == retry) { | |
266 | printf("%s: card is still busy\n", __func__); | |
f53c4e4b | 267 | writel(mask, &priv->reg->norintsts); |
915ffa52 | 268 | return -ETIMEDOUT; |
21ef6a10 TW |
269 | } |
270 | ||
f53c4e4b | 271 | cmd->response[0] = readl(&priv->reg->rspreg0); |
21ef6a10 TW |
272 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); |
273 | } else { | |
f53c4e4b | 274 | cmd->response[0] = readl(&priv->reg->rspreg0); |
21ef6a10 TW |
275 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); |
276 | } | |
277 | } | |
278 | ||
279 | if (data) { | |
9b3d1873 A |
280 | unsigned long start = get_timer(0); |
281 | ||
21ef6a10 | 282 | while (1) { |
f53c4e4b | 283 | mask = readl(&priv->reg->norintsts); |
21ef6a10 | 284 | |
8e42f0d6 | 285 | if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 | 286 | /* Error Interrupt */ |
f53c4e4b | 287 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
288 | printf("%s: error during transfer: 0x%08x\n", |
289 | __func__, mask); | |
290 | return -1; | |
8e42f0d6 | 291 | } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { |
5a762e25 A |
292 | /* |
293 | * DMA Interrupt, restart the transfer where | |
294 | * it was interrupted. | |
295 | */ | |
f53c4e4b | 296 | unsigned int address = readl(&priv->reg->sysad); |
5a762e25 | 297 | |
21ef6a10 | 298 | debug("DMA end\n"); |
5a762e25 | 299 | writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, |
f53c4e4b SW |
300 | &priv->reg->norintsts); |
301 | writel(address, &priv->reg->sysad); | |
8e42f0d6 | 302 | } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { |
21ef6a10 TW |
303 | /* Transfer Complete */ |
304 | debug("r/w is done\n"); | |
305 | break; | |
09fb7361 | 306 | } else if (get_timer(start) > 8000UL) { |
f53c4e4b | 307 | writel(mask, &priv->reg->norintsts); |
9b3d1873 A |
308 | printf("%s: MMC Timeout\n" |
309 | " Interrupt status 0x%08x\n" | |
310 | " Interrupt status enable 0x%08x\n" | |
311 | " Interrupt signal enable 0x%08x\n" | |
312 | " Present status 0x%08x\n", | |
313 | __func__, mask, | |
f53c4e4b SW |
314 | readl(&priv->reg->norintstsen), |
315 | readl(&priv->reg->norintsigen), | |
316 | readl(&priv->reg->prnsts)); | |
9b3d1873 | 317 | return -1; |
21ef6a10 TW |
318 | } |
319 | } | |
f53c4e4b | 320 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
321 | } |
322 | ||
323 | udelay(1000); | |
324 | return 0; | |
325 | } | |
326 | ||
ab769f22 | 327 | static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, |
f53c4e4b | 328 | struct mmc_data *data) |
19815399 SW |
329 | { |
330 | void *buf; | |
331 | unsigned int bbflags; | |
332 | size_t len; | |
333 | struct bounce_buffer bbstate; | |
334 | int ret; | |
335 | ||
336 | if (data) { | |
337 | if (data->flags & MMC_DATA_READ) { | |
338 | buf = data->dest; | |
339 | bbflags = GEN_BB_WRITE; | |
340 | } else { | |
341 | buf = (void *)data->src; | |
342 | bbflags = GEN_BB_READ; | |
343 | } | |
344 | len = data->blocks * data->blocksize; | |
345 | ||
346 | bounce_buffer_start(&bbstate, buf, len, bbflags); | |
347 | } | |
348 | ||
f53c4e4b | 349 | ret = tegra_mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); |
19815399 SW |
350 | |
351 | if (data) | |
352 | bounce_buffer_stop(&bbstate); | |
353 | ||
354 | return ret; | |
355 | } | |
356 | ||
f53c4e4b | 357 | static void tegra_mmc_change_clock(struct tegra_mmc_priv *priv, uint clock) |
21ef6a10 | 358 | { |
e8adca9e | 359 | ulong rate; |
4ed59e70 | 360 | int div; |
21ef6a10 TW |
361 | unsigned short clk; |
362 | unsigned long timeout; | |
4ed59e70 | 363 | |
21ef6a10 TW |
364 | debug(" mmc_change_clock called\n"); |
365 | ||
4ed59e70 | 366 | /* |
2d348a16 | 367 | * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 |
4ed59e70 | 368 | */ |
21ef6a10 TW |
369 | if (clock == 0) |
370 | goto out; | |
e8adca9e SW |
371 | |
372 | rate = clk_set_rate(&priv->clk, clock); | |
373 | div = (rate + clock - 1) / clock; | |
4ed59e70 | 374 | debug("div = %d\n", div); |
21ef6a10 | 375 | |
f53c4e4b | 376 | writew(0, &priv->reg->clkcon); |
21ef6a10 | 377 | |
21ef6a10 TW |
378 | /* |
379 | * CLKCON | |
380 | * SELFREQ[15:8] : base clock divided by value | |
381 | * ENSDCLK[2] : SD Clock Enable | |
382 | * STBLINTCLK[1] : Internal Clock Stable | |
383 | * ENINTCLK[0] : Internal Clock Enable | |
384 | */ | |
4ed59e70 | 385 | div >>= 1; |
8e42f0d6 A |
386 | clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | |
387 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); | |
f53c4e4b | 388 | writew(clk, &priv->reg->clkcon); |
21ef6a10 TW |
389 | |
390 | /* Wait max 10 ms */ | |
391 | timeout = 10; | |
f53c4e4b | 392 | while (!(readw(&priv->reg->clkcon) & |
8e42f0d6 | 393 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { |
21ef6a10 TW |
394 | if (timeout == 0) { |
395 | printf("%s: timeout error\n", __func__); | |
396 | return; | |
397 | } | |
398 | timeout--; | |
399 | udelay(1000); | |
400 | } | |
401 | ||
8e42f0d6 | 402 | clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; |
f53c4e4b | 403 | writew(clk, &priv->reg->clkcon); |
21ef6a10 TW |
404 | |
405 | debug("mmc_change_clock: clkcon = %08X\n", clk); | |
21ef6a10 TW |
406 | |
407 | out: | |
f53c4e4b | 408 | priv->clock = clock; |
21ef6a10 TW |
409 | } |
410 | ||
07b0b9c0 | 411 | static int tegra_mmc_set_ios(struct mmc *mmc) |
21ef6a10 | 412 | { |
f53c4e4b | 413 | struct tegra_mmc_priv *priv = mmc->priv; |
21ef6a10 TW |
414 | unsigned char ctrl; |
415 | debug(" mmc_set_ios called\n"); | |
416 | ||
417 | debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); | |
418 | ||
419 | /* Change clock first */ | |
f53c4e4b | 420 | tegra_mmc_change_clock(priv, mmc->clock); |
21ef6a10 | 421 | |
f53c4e4b | 422 | ctrl = readb(&priv->reg->hostctl); |
21ef6a10 TW |
423 | |
424 | /* | |
425 | * WIDE8[5] | |
426 | * 0 = Depend on WIDE4 | |
427 | * 1 = 8-bit mode | |
428 | * WIDE4[1] | |
429 | * 1 = 4-bit mode | |
430 | * 0 = 1-bit mode | |
431 | */ | |
432 | if (mmc->bus_width == 8) | |
433 | ctrl |= (1 << 5); | |
434 | else if (mmc->bus_width == 4) | |
435 | ctrl |= (1 << 1); | |
436 | else | |
437 | ctrl &= ~(1 << 1); | |
438 | ||
f53c4e4b | 439 | writeb(ctrl, &priv->reg->hostctl); |
21ef6a10 | 440 | debug("mmc_set_ios: hostctl = %08X\n", ctrl); |
07b0b9c0 JC |
441 | |
442 | return 0; | |
21ef6a10 TW |
443 | } |
444 | ||
f53c4e4b | 445 | static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv) |
6b83588e SW |
446 | { |
447 | #if defined(CONFIG_TEGRA30) | |
6b83588e SW |
448 | u32 val; |
449 | ||
f53c4e4b | 450 | debug("%s: sdmmc address = %08x\n", __func__, (unsigned int)priv->reg); |
6b83588e SW |
451 | |
452 | /* Set the pad drive strength for SDMMC1 or 3 only */ | |
f53c4e4b SW |
453 | if (priv->reg != (void *)0x78000000 && |
454 | priv->reg != (void *)0x78000400) { | |
6b83588e SW |
455 | debug("%s: settings are only valid for SDMMC1/SDMMC3!\n", |
456 | __func__); | |
457 | return; | |
458 | } | |
459 | ||
f53c4e4b | 460 | val = readl(&priv->reg->sdmemcmppadctl); |
6b83588e SW |
461 | val &= 0xFFFFFFF0; |
462 | val |= MEMCOMP_PADCTRL_VREF; | |
f53c4e4b | 463 | writel(val, &priv->reg->sdmemcmppadctl); |
6b83588e | 464 | |
f53c4e4b | 465 | val = readl(&priv->reg->autocalcfg); |
6b83588e SW |
466 | val &= 0xFFFF0000; |
467 | val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED; | |
f53c4e4b | 468 | writel(val, &priv->reg->autocalcfg); |
6b83588e SW |
469 | #endif |
470 | } | |
471 | ||
f53c4e4b | 472 | static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc) |
21ef6a10 TW |
473 | { |
474 | unsigned int timeout; | |
475 | debug(" mmc_reset called\n"); | |
476 | ||
477 | /* | |
478 | * RSTALL[0] : Software reset for all | |
479 | * 1 = reset | |
480 | * 0 = work | |
481 | */ | |
f53c4e4b | 482 | writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &priv->reg->swrst); |
21ef6a10 | 483 | |
f53c4e4b | 484 | priv->clock = 0; |
21ef6a10 TW |
485 | |
486 | /* Wait max 100 ms */ | |
487 | timeout = 100; | |
488 | ||
489 | /* hw clears the bit when it's done */ | |
f53c4e4b | 490 | while (readb(&priv->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { |
21ef6a10 TW |
491 | if (timeout == 0) { |
492 | printf("%s: timeout error\n", __func__); | |
493 | return; | |
494 | } | |
495 | timeout--; | |
496 | udelay(1000); | |
497 | } | |
2d348a16 TW |
498 | |
499 | /* Set SD bus voltage & enable bus power */ | |
f53c4e4b | 500 | tegra_mmc_set_power(priv, fls(mmc->cfg->voltages) - 1); |
2d348a16 | 501 | debug("%s: power control = %02X, host control = %02X\n", __func__, |
f53c4e4b | 502 | readb(&priv->reg->pwrcon), readb(&priv->reg->hostctl)); |
2d348a16 TW |
503 | |
504 | /* Make sure SDIO pads are set up */ | |
f53c4e4b | 505 | tegra_mmc_pad_init(priv); |
21ef6a10 TW |
506 | } |
507 | ||
6a474db4 | 508 | static int tegra_mmc_init(struct mmc *mmc) |
21ef6a10 | 509 | { |
f53c4e4b | 510 | struct tegra_mmc_priv *priv = mmc->priv; |
21ef6a10 | 511 | unsigned int mask; |
6a474db4 | 512 | debug(" tegra_mmc_init called\n"); |
21ef6a10 | 513 | |
f53c4e4b | 514 | tegra_mmc_reset(priv, mmc); |
21ef6a10 | 515 | |
4119b709 MZ |
516 | #if defined(CONFIG_TEGRA124_MMC_DISABLE_EXT_LOOPBACK) |
517 | /* | |
518 | * Disable the external clock loopback and use the internal one on | |
519 | * SDMMC3 as per the SDMMC_VENDOR_MISC_CNTRL_0 register's SDMMC_SPARE1 | |
520 | * bits being set to 0xfffd according to the TRM. | |
521 | * | |
522 | * TODO(marcel.ziswiler@toradex.com): Move to device tree controlled | |
523 | * approach once proper kernel integration made it mainline. | |
524 | */ | |
525 | if (priv->reg == (void *)0x700b0400) { | |
526 | mask = readl(&priv->reg->venmiscctl); | |
527 | mask &= ~TEGRA_MMC_MISCON_ENABLE_EXT_LOOPBACK; | |
528 | writel(mask, &priv->reg->venmiscctl); | |
529 | } | |
530 | #endif | |
531 | ||
f53c4e4b SW |
532 | priv->version = readw(&priv->reg->hcver); |
533 | debug("host version = %x\n", priv->version); | |
21ef6a10 TW |
534 | |
535 | /* mask all */ | |
f53c4e4b SW |
536 | writel(0xffffffff, &priv->reg->norintstsen); |
537 | writel(0xffffffff, &priv->reg->norintsigen); | |
21ef6a10 | 538 | |
f53c4e4b | 539 | writeb(0xe, &priv->reg->timeoutcon); /* TMCLK * 2^27 */ |
21ef6a10 TW |
540 | /* |
541 | * NORMAL Interrupt Status Enable Register init | |
542 | * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable | |
543 | * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable | |
5a762e25 | 544 | * [3] ENSTADMAINT : DMA boundary interrupt |
21ef6a10 TW |
545 | * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable |
546 | * [0] ENSTACMDCMPLT : Command Complete Status Enable | |
547 | */ | |
f53c4e4b | 548 | mask = readl(&priv->reg->norintstsen); |
21ef6a10 | 549 | mask &= ~(0xffff); |
8e42f0d6 A |
550 | mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | |
551 | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | | |
5a762e25 | 552 | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | |
8e42f0d6 A |
553 | TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | |
554 | TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); | |
f53c4e4b | 555 | writel(mask, &priv->reg->norintstsen); |
21ef6a10 TW |
556 | |
557 | /* | |
558 | * NORMAL Interrupt Signal Enable Register init | |
559 | * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable | |
560 | */ | |
f53c4e4b | 561 | mask = readl(&priv->reg->norintsigen); |
21ef6a10 | 562 | mask &= ~(0xffff); |
8e42f0d6 | 563 | mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; |
f53c4e4b | 564 | writel(mask, &priv->reg->norintsigen); |
21ef6a10 TW |
565 | |
566 | return 0; | |
567 | } | |
568 | ||
19d7bf3d | 569 | static int tegra_mmc_getcd(struct mmc *mmc) |
bf83662b | 570 | { |
f53c4e4b | 571 | struct tegra_mmc_priv *priv = mmc->priv; |
bf83662b | 572 | |
29f3e3f2 | 573 | debug("tegra_mmc_getcd called\n"); |
bf83662b | 574 | |
f53c4e4b SW |
575 | if (dm_gpio_is_valid(&priv->cd_gpio)) |
576 | return dm_gpio_get_value(&priv->cd_gpio); | |
bf83662b TR |
577 | |
578 | return 1; | |
579 | } | |
580 | ||
ab769f22 PA |
581 | static const struct mmc_ops tegra_mmc_ops = { |
582 | .send_cmd = tegra_mmc_send_cmd, | |
583 | .set_ios = tegra_mmc_set_ios, | |
6a474db4 | 584 | .init = tegra_mmc_init, |
ab769f22 PA |
585 | .getcd = tegra_mmc_getcd, |
586 | }; | |
587 | ||
6a474db4 | 588 | static int tegra_mmc_probe(struct udevice *dev) |
21ef6a10 | 589 | { |
6a474db4 TW |
590 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
591 | struct tegra_mmc_priv *priv = dev_get_priv(dev); | |
e8adca9e | 592 | int bus_width, ret; |
21ef6a10 | 593 | |
f53c4e4b SW |
594 | priv->cfg.name = "Tegra SD/MMC"; |
595 | priv->cfg.ops = &tegra_mmc_ops; | |
21ef6a10 | 596 | |
e160f7d4 SG |
597 | bus_width = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), |
598 | "bus-width", 1); | |
6a474db4 | 599 | |
f53c4e4b SW |
600 | priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; |
601 | priv->cfg.host_caps = 0; | |
6a474db4 | 602 | if (bus_width == 8) |
f53c4e4b | 603 | priv->cfg.host_caps |= MMC_MODE_8BIT; |
6a474db4 | 604 | if (bus_width >= 4) |
f53c4e4b SW |
605 | priv->cfg.host_caps |= MMC_MODE_4BIT; |
606 | priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; | |
21ef6a10 TW |
607 | |
608 | /* | |
609 | * min freq is for card identification, and is the highest | |
610 | * low-speed SDIO card frequency (actually 400KHz) | |
611 | * max freq is highest HS eMMC clock as per the SD/MMC spec | |
612 | * (actually 52MHz) | |
21ef6a10 | 613 | */ |
f53c4e4b SW |
614 | priv->cfg.f_min = 375000; |
615 | priv->cfg.f_max = 48000000; | |
21ef6a10 | 616 | |
f53c4e4b | 617 | priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; |
93bfd616 | 618 | |
a821c4af | 619 | priv->reg = (void *)devfdt_get_addr(dev); |
c9aa831e | 620 | |
6a474db4 TW |
621 | ret = reset_get_by_name(dev, "sdhci", &priv->reset_ctl); |
622 | if (ret) { | |
623 | debug("reset_get_by_name() failed: %d\n", ret); | |
624 | return ret; | |
c0493076 | 625 | } |
6a474db4 TW |
626 | ret = clk_get_by_index(dev, 0, &priv->clk); |
627 | if (ret) { | |
628 | debug("clk_get_by_index() failed: %d\n", ret); | |
629 | return ret; | |
630 | } | |
631 | ||
632 | ret = reset_assert(&priv->reset_ctl); | |
633 | if (ret) | |
634 | return ret; | |
635 | ret = clk_enable(&priv->clk); | |
636 | if (ret) | |
637 | return ret; | |
638 | ret = clk_set_rate(&priv->clk, 20000000); | |
639 | if (IS_ERR_VALUE(ret)) | |
640 | return ret; | |
641 | ret = reset_deassert(&priv->reset_ctl); | |
642 | if (ret) | |
643 | return ret; | |
c9aa831e | 644 | |
6a474db4 TW |
645 | /* These GPIOs are optional */ |
646 | gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, | |
647 | GPIOD_IS_IN); | |
648 | gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, | |
649 | GPIOD_IS_IN); | |
650 | gpio_request_by_name(dev, "power-gpios", 0, | |
651 | &priv->pwr_gpio, GPIOD_IS_OUT); | |
652 | if (dm_gpio_is_valid(&priv->pwr_gpio)) | |
653 | dm_gpio_set_value(&priv->pwr_gpio, 1); | |
c9aa831e | 654 | |
6a474db4 TW |
655 | priv->mmc = mmc_create(&priv->cfg, priv); |
656 | if (priv->mmc == NULL) | |
657 | return -1; | |
c9aa831e | 658 | |
6a474db4 TW |
659 | priv->mmc->dev = dev; |
660 | upriv->mmc = priv->mmc; | |
c9aa831e | 661 | |
c9aa831e TW |
662 | return 0; |
663 | } | |
664 | ||
6a474db4 TW |
665 | static const struct udevice_id tegra_mmc_ids[] = { |
666 | { .compatible = "nvidia,tegra20-sdhci" }, | |
667 | { .compatible = "nvidia,tegra30-sdhci" }, | |
668 | { .compatible = "nvidia,tegra114-sdhci" }, | |
669 | { .compatible = "nvidia,tegra124-sdhci" }, | |
670 | { .compatible = "nvidia,tegra210-sdhci" }, | |
671 | { .compatible = "nvidia,tegra186-sdhci" }, | |
672 | { } | |
673 | }; | |
c9aa831e | 674 | |
6a474db4 TW |
675 | U_BOOT_DRIVER(tegra_mmc_drv) = { |
676 | .name = "tegra_mmc", | |
677 | .id = UCLASS_MMC, | |
678 | .of_match = tegra_mmc_ids, | |
679 | .probe = tegra_mmc_probe, | |
680 | .priv_auto_alloc_size = sizeof(struct tegra_mmc_priv), | |
681 | }; |