]>
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> | |
7aaa5a60 | 5 | * Portions Copyright 2011-2015 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> |
9877841f | 12 | #include <asm/gpio.h> |
21ef6a10 | 13 | #include <asm/io.h> |
39f63332 | 14 | #ifndef CONFIG_TEGRA186 |
4ed59e70 | 15 | #include <asm/arch/clock.h> |
150c2493 | 16 | #include <asm/arch-tegra/clk_rst.h> |
39f63332 | 17 | #endif |
19d7bf3d | 18 | #include <asm/arch-tegra/mmc.h> |
150c2493 TW |
19 | #include <asm/arch-tegra/tegra_mmc.h> |
20 | #include <mmc.h> | |
21ef6a10 | 21 | |
c9aa831e | 22 | DECLARE_GLOBAL_DATA_PTR; |
21ef6a10 | 23 | |
f175603f | 24 | struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; |
4ed59e70 | 25 | |
0f925822 | 26 | #if !CONFIG_IS_ENABLED(OF_CONTROL) |
c9aa831e TW |
27 | #error "Please enable device tree support to use this driver" |
28 | #endif | |
21ef6a10 | 29 | |
2d348a16 TW |
30 | static void mmc_set_power(struct mmc_host *host, unsigned short power) |
31 | { | |
32 | u8 pwr = 0; | |
33 | debug("%s: power = %x\n", __func__, power); | |
34 | ||
35 | if (power != (unsigned short)-1) { | |
36 | switch (1 << power) { | |
37 | case MMC_VDD_165_195: | |
38 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; | |
39 | break; | |
40 | case MMC_VDD_29_30: | |
41 | case MMC_VDD_30_31: | |
42 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; | |
43 | break; | |
44 | case MMC_VDD_32_33: | |
45 | case MMC_VDD_33_34: | |
46 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; | |
47 | break; | |
48 | } | |
49 | } | |
50 | debug("%s: pwr = %X\n", __func__, pwr); | |
51 | ||
52 | /* Set the bus voltage first (if any) */ | |
53 | writeb(pwr, &host->reg->pwrcon); | |
54 | if (pwr == 0) | |
55 | return; | |
56 | ||
57 | /* Now enable bus power */ | |
58 | pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; | |
59 | writeb(pwr, &host->reg->pwrcon); | |
60 | } | |
61 | ||
19815399 SW |
62 | static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, |
63 | struct bounce_buffer *bbstate) | |
21ef6a10 TW |
64 | { |
65 | unsigned char ctrl; | |
66 | ||
21ef6a10 | 67 | |
19815399 SW |
68 | debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", |
69 | bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, | |
70 | data->blocksize); | |
71 | ||
c39e2a75 | 72 | writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad); |
21ef6a10 TW |
73 | /* |
74 | * DMASEL[4:3] | |
75 | * 00 = Selects SDMA | |
76 | * 01 = Reserved | |
77 | * 10 = Selects 32-bit Address ADMA2 | |
78 | * 11 = Selects 64-bit Address ADMA2 | |
79 | */ | |
80 | ctrl = readb(&host->reg->hostctl); | |
8e42f0d6 A |
81 | ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; |
82 | ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; | |
21ef6a10 TW |
83 | writeb(ctrl, &host->reg->hostctl); |
84 | ||
85 | /* We do not handle DMA boundaries, so set it to max (512 KiB) */ | |
86 | writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); | |
87 | writew(data->blocks, &host->reg->blkcnt); | |
88 | } | |
89 | ||
90 | static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) | |
91 | { | |
92 | unsigned short mode; | |
93 | debug(" mmc_set_transfer_mode called\n"); | |
94 | /* | |
95 | * TRNMOD | |
96 | * MUL1SIN0[5] : Multi/Single Block Select | |
97 | * RD1WT0[4] : Data Transfer Direction Select | |
98 | * 1 = read | |
99 | * 0 = write | |
100 | * ENACMD12[2] : Auto CMD12 Enable | |
101 | * ENBLKCNT[1] : Block Count Enable | |
102 | * ENDMA[0] : DMA Enable | |
103 | */ | |
8e42f0d6 A |
104 | mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | |
105 | TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); | |
106 | ||
21ef6a10 | 107 | if (data->blocks > 1) |
8e42f0d6 A |
108 | mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; |
109 | ||
21ef6a10 | 110 | if (data->flags & MMC_DATA_READ) |
8e42f0d6 | 111 | mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; |
21ef6a10 TW |
112 | |
113 | writew(mode, &host->reg->trnmod); | |
114 | } | |
115 | ||
0963ff3a A |
116 | static int mmc_wait_inhibit(struct mmc_host *host, |
117 | struct mmc_cmd *cmd, | |
118 | struct mmc_data *data, | |
119 | unsigned int timeout) | |
21ef6a10 | 120 | { |
21ef6a10 TW |
121 | /* |
122 | * PRNSTS | |
0963ff3a A |
123 | * CMDINHDAT[1] : Command Inhibit (DAT) |
124 | * CMDINHCMD[0] : Command Inhibit (CMD) | |
21ef6a10 | 125 | */ |
0963ff3a | 126 | unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; |
21ef6a10 TW |
127 | |
128 | /* | |
129 | * We shouldn't wait for data inhibit for stop commands, even | |
130 | * though they might use busy signaling | |
131 | */ | |
0963ff3a A |
132 | if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) |
133 | mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; | |
21ef6a10 TW |
134 | |
135 | while (readl(&host->reg->prnsts) & mask) { | |
136 | if (timeout == 0) { | |
137 | printf("%s: timeout error\n", __func__); | |
138 | return -1; | |
139 | } | |
140 | timeout--; | |
141 | udelay(1000); | |
142 | } | |
143 | ||
0963ff3a A |
144 | return 0; |
145 | } | |
146 | ||
19815399 SW |
147 | static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, |
148 | struct mmc_data *data, struct bounce_buffer *bbstate) | |
0963ff3a | 149 | { |
93bfd616 | 150 | struct mmc_host *host = mmc->priv; |
0963ff3a A |
151 | int flags, i; |
152 | int result; | |
60e242ed | 153 | unsigned int mask = 0; |
0963ff3a A |
154 | unsigned int retry = 0x100000; |
155 | debug(" mmc_send_cmd called\n"); | |
156 | ||
157 | result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); | |
158 | ||
159 | if (result < 0) | |
160 | return result; | |
161 | ||
21ef6a10 | 162 | if (data) |
19815399 | 163 | mmc_prepare_data(host, data, bbstate); |
21ef6a10 TW |
164 | |
165 | debug("cmd->arg: %08x\n", cmd->cmdarg); | |
166 | writel(cmd->cmdarg, &host->reg->argument); | |
167 | ||
168 | if (data) | |
169 | mmc_set_transfer_mode(host, data); | |
170 | ||
171 | if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) | |
172 | return -1; | |
173 | ||
174 | /* | |
175 | * CMDREG | |
176 | * CMDIDX[13:8] : Command index | |
177 | * DATAPRNT[5] : Data Present Select | |
178 | * ENCMDIDX[4] : Command Index Check Enable | |
179 | * ENCMDCRC[3] : Command CRC Check Enable | |
180 | * RSPTYP[1:0] | |
181 | * 00 = No Response | |
182 | * 01 = Length 136 | |
183 | * 10 = Length 48 | |
184 | * 11 = Length 48 Check busy after response | |
185 | */ | |
186 | if (!(cmd->resp_type & MMC_RSP_PRESENT)) | |
8e42f0d6 | 187 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; |
21ef6a10 | 188 | else if (cmd->resp_type & MMC_RSP_136) |
8e42f0d6 | 189 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; |
21ef6a10 | 190 | else if (cmd->resp_type & MMC_RSP_BUSY) |
8e42f0d6 | 191 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; |
21ef6a10 | 192 | else |
8e42f0d6 | 193 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; |
21ef6a10 TW |
194 | |
195 | if (cmd->resp_type & MMC_RSP_CRC) | |
8e42f0d6 | 196 | flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; |
21ef6a10 | 197 | if (cmd->resp_type & MMC_RSP_OPCODE) |
8e42f0d6 | 198 | flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; |
21ef6a10 | 199 | if (data) |
8e42f0d6 | 200 | flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; |
21ef6a10 TW |
201 | |
202 | debug("cmd: %d\n", cmd->cmdidx); | |
203 | ||
204 | writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); | |
205 | ||
206 | for (i = 0; i < retry; i++) { | |
207 | mask = readl(&host->reg->norintsts); | |
208 | /* Command Complete */ | |
8e42f0d6 | 209 | if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { |
21ef6a10 TW |
210 | if (!data) |
211 | writel(mask, &host->reg->norintsts); | |
212 | break; | |
213 | } | |
214 | } | |
215 | ||
216 | if (i == retry) { | |
217 | printf("%s: waiting for status update\n", __func__); | |
cf39cf55 | 218 | writel(mask, &host->reg->norintsts); |
21ef6a10 TW |
219 | return TIMEOUT; |
220 | } | |
221 | ||
8e42f0d6 | 222 | if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { |
21ef6a10 TW |
223 | /* Timeout Error */ |
224 | debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); | |
cf39cf55 | 225 | writel(mask, &host->reg->norintsts); |
21ef6a10 | 226 | return TIMEOUT; |
8e42f0d6 | 227 | } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 TW |
228 | /* Error Interrupt */ |
229 | debug("error: %08x cmd %d\n", mask, cmd->cmdidx); | |
cf39cf55 | 230 | writel(mask, &host->reg->norintsts); |
21ef6a10 TW |
231 | return -1; |
232 | } | |
233 | ||
234 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
235 | if (cmd->resp_type & MMC_RSP_136) { | |
236 | /* CRC is stripped so we need to do some shifting. */ | |
237 | for (i = 0; i < 4; i++) { | |
c39e2a75 TR |
238 | unsigned long offset = |
239 | (unsigned long)(&host->reg->rspreg3 - i); | |
21ef6a10 TW |
240 | cmd->response[i] = readl(offset) << 8; |
241 | ||
242 | if (i != 3) { | |
243 | cmd->response[i] |= | |
244 | readb(offset - 1); | |
245 | } | |
246 | debug("cmd->resp[%d]: %08x\n", | |
247 | i, cmd->response[i]); | |
248 | } | |
249 | } else if (cmd->resp_type & MMC_RSP_BUSY) { | |
250 | for (i = 0; i < retry; i++) { | |
251 | /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ | |
252 | if (readl(&host->reg->prnsts) | |
253 | & (1 << 20)) /* DAT[0] */ | |
254 | break; | |
255 | } | |
256 | ||
257 | if (i == retry) { | |
258 | printf("%s: card is still busy\n", __func__); | |
cf39cf55 | 259 | writel(mask, &host->reg->norintsts); |
21ef6a10 TW |
260 | return TIMEOUT; |
261 | } | |
262 | ||
263 | cmd->response[0] = readl(&host->reg->rspreg0); | |
264 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); | |
265 | } else { | |
266 | cmd->response[0] = readl(&host->reg->rspreg0); | |
267 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); | |
268 | } | |
269 | } | |
270 | ||
271 | if (data) { | |
9b3d1873 A |
272 | unsigned long start = get_timer(0); |
273 | ||
21ef6a10 TW |
274 | while (1) { |
275 | mask = readl(&host->reg->norintsts); | |
276 | ||
8e42f0d6 | 277 | if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 TW |
278 | /* Error Interrupt */ |
279 | writel(mask, &host->reg->norintsts); | |
280 | printf("%s: error during transfer: 0x%08x\n", | |
281 | __func__, mask); | |
282 | return -1; | |
8e42f0d6 | 283 | } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { |
5a762e25 A |
284 | /* |
285 | * DMA Interrupt, restart the transfer where | |
286 | * it was interrupted. | |
287 | */ | |
288 | unsigned int address = readl(&host->reg->sysad); | |
289 | ||
21ef6a10 | 290 | debug("DMA end\n"); |
5a762e25 A |
291 | writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, |
292 | &host->reg->norintsts); | |
293 | writel(address, &host->reg->sysad); | |
8e42f0d6 | 294 | } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { |
21ef6a10 TW |
295 | /* Transfer Complete */ |
296 | debug("r/w is done\n"); | |
297 | break; | |
09fb7361 | 298 | } else if (get_timer(start) > 8000UL) { |
9b3d1873 A |
299 | writel(mask, &host->reg->norintsts); |
300 | printf("%s: MMC Timeout\n" | |
301 | " Interrupt status 0x%08x\n" | |
302 | " Interrupt status enable 0x%08x\n" | |
303 | " Interrupt signal enable 0x%08x\n" | |
304 | " Present status 0x%08x\n", | |
305 | __func__, mask, | |
306 | readl(&host->reg->norintstsen), | |
307 | readl(&host->reg->norintsigen), | |
308 | readl(&host->reg->prnsts)); | |
309 | return -1; | |
21ef6a10 TW |
310 | } |
311 | } | |
312 | writel(mask, &host->reg->norintsts); | |
313 | } | |
314 | ||
315 | udelay(1000); | |
316 | return 0; | |
317 | } | |
318 | ||
ab769f22 | 319 | static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, |
19815399 SW |
320 | struct mmc_data *data) |
321 | { | |
322 | void *buf; | |
323 | unsigned int bbflags; | |
324 | size_t len; | |
325 | struct bounce_buffer bbstate; | |
326 | int ret; | |
327 | ||
328 | if (data) { | |
329 | if (data->flags & MMC_DATA_READ) { | |
330 | buf = data->dest; | |
331 | bbflags = GEN_BB_WRITE; | |
332 | } else { | |
333 | buf = (void *)data->src; | |
334 | bbflags = GEN_BB_READ; | |
335 | } | |
336 | len = data->blocks * data->blocksize; | |
337 | ||
338 | bounce_buffer_start(&bbstate, buf, len, bbflags); | |
339 | } | |
340 | ||
341 | ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); | |
342 | ||
343 | if (data) | |
344 | bounce_buffer_stop(&bbstate); | |
345 | ||
346 | return ret; | |
347 | } | |
348 | ||
21ef6a10 TW |
349 | static void mmc_change_clock(struct mmc_host *host, uint clock) |
350 | { | |
4ed59e70 | 351 | int div; |
21ef6a10 TW |
352 | unsigned short clk; |
353 | unsigned long timeout; | |
4ed59e70 | 354 | |
21ef6a10 TW |
355 | debug(" mmc_change_clock called\n"); |
356 | ||
4ed59e70 | 357 | /* |
2d348a16 | 358 | * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 |
4ed59e70 | 359 | */ |
21ef6a10 TW |
360 | if (clock == 0) |
361 | goto out; | |
39f63332 | 362 | #ifndef CONFIG_TEGRA186 |
4ed59e70 SG |
363 | clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, |
364 | &div); | |
39f63332 SW |
365 | #else |
366 | div = (20000000 + clock - 1) / clock; | |
367 | #endif | |
4ed59e70 | 368 | debug("div = %d\n", div); |
21ef6a10 TW |
369 | |
370 | writew(0, &host->reg->clkcon); | |
371 | ||
21ef6a10 TW |
372 | /* |
373 | * CLKCON | |
374 | * SELFREQ[15:8] : base clock divided by value | |
375 | * ENSDCLK[2] : SD Clock Enable | |
376 | * STBLINTCLK[1] : Internal Clock Stable | |
377 | * ENINTCLK[0] : Internal Clock Enable | |
378 | */ | |
4ed59e70 | 379 | div >>= 1; |
8e42f0d6 A |
380 | clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | |
381 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); | |
21ef6a10 TW |
382 | writew(clk, &host->reg->clkcon); |
383 | ||
384 | /* Wait max 10 ms */ | |
385 | timeout = 10; | |
8e42f0d6 A |
386 | while (!(readw(&host->reg->clkcon) & |
387 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { | |
21ef6a10 TW |
388 | if (timeout == 0) { |
389 | printf("%s: timeout error\n", __func__); | |
390 | return; | |
391 | } | |
392 | timeout--; | |
393 | udelay(1000); | |
394 | } | |
395 | ||
8e42f0d6 | 396 | clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; |
21ef6a10 TW |
397 | writew(clk, &host->reg->clkcon); |
398 | ||
399 | debug("mmc_change_clock: clkcon = %08X\n", clk); | |
21ef6a10 TW |
400 | |
401 | out: | |
402 | host->clock = clock; | |
403 | } | |
404 | ||
ab769f22 | 405 | static void tegra_mmc_set_ios(struct mmc *mmc) |
21ef6a10 TW |
406 | { |
407 | struct mmc_host *host = mmc->priv; | |
408 | unsigned char ctrl; | |
409 | debug(" mmc_set_ios called\n"); | |
410 | ||
411 | debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); | |
412 | ||
413 | /* Change clock first */ | |
21ef6a10 TW |
414 | mmc_change_clock(host, mmc->clock); |
415 | ||
416 | ctrl = readb(&host->reg->hostctl); | |
417 | ||
418 | /* | |
419 | * WIDE8[5] | |
420 | * 0 = Depend on WIDE4 | |
421 | * 1 = 8-bit mode | |
422 | * WIDE4[1] | |
423 | * 1 = 4-bit mode | |
424 | * 0 = 1-bit mode | |
425 | */ | |
426 | if (mmc->bus_width == 8) | |
427 | ctrl |= (1 << 5); | |
428 | else if (mmc->bus_width == 4) | |
429 | ctrl |= (1 << 1); | |
430 | else | |
431 | ctrl &= ~(1 << 1); | |
432 | ||
433 | writeb(ctrl, &host->reg->hostctl); | |
434 | debug("mmc_set_ios: hostctl = %08X\n", ctrl); | |
435 | } | |
436 | ||
2d348a16 | 437 | static void mmc_reset(struct mmc_host *host, struct mmc *mmc) |
21ef6a10 TW |
438 | { |
439 | unsigned int timeout; | |
440 | debug(" mmc_reset called\n"); | |
441 | ||
442 | /* | |
443 | * RSTALL[0] : Software reset for all | |
444 | * 1 = reset | |
445 | * 0 = work | |
446 | */ | |
8e42f0d6 | 447 | writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); |
21ef6a10 TW |
448 | |
449 | host->clock = 0; | |
450 | ||
451 | /* Wait max 100 ms */ | |
452 | timeout = 100; | |
453 | ||
454 | /* hw clears the bit when it's done */ | |
8e42f0d6 | 455 | while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { |
21ef6a10 TW |
456 | if (timeout == 0) { |
457 | printf("%s: timeout error\n", __func__); | |
458 | return; | |
459 | } | |
460 | timeout--; | |
461 | udelay(1000); | |
462 | } | |
2d348a16 TW |
463 | |
464 | /* Set SD bus voltage & enable bus power */ | |
93bfd616 | 465 | mmc_set_power(host, fls(mmc->cfg->voltages) - 1); |
2d348a16 TW |
466 | debug("%s: power control = %02X, host control = %02X\n", __func__, |
467 | readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); | |
468 | ||
469 | /* Make sure SDIO pads are set up */ | |
470 | pad_init_mmc(host); | |
21ef6a10 TW |
471 | } |
472 | ||
ab769f22 | 473 | static int tegra_mmc_core_init(struct mmc *mmc) |
21ef6a10 | 474 | { |
93bfd616 | 475 | struct mmc_host *host = mmc->priv; |
21ef6a10 TW |
476 | unsigned int mask; |
477 | debug(" mmc_core_init called\n"); | |
478 | ||
2d348a16 | 479 | mmc_reset(host, mmc); |
21ef6a10 TW |
480 | |
481 | host->version = readw(&host->reg->hcver); | |
482 | debug("host version = %x\n", host->version); | |
483 | ||
484 | /* mask all */ | |
485 | writel(0xffffffff, &host->reg->norintstsen); | |
486 | writel(0xffffffff, &host->reg->norintsigen); | |
487 | ||
488 | writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ | |
489 | /* | |
490 | * NORMAL Interrupt Status Enable Register init | |
491 | * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable | |
492 | * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable | |
5a762e25 | 493 | * [3] ENSTADMAINT : DMA boundary interrupt |
21ef6a10 TW |
494 | * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable |
495 | * [0] ENSTACMDCMPLT : Command Complete Status Enable | |
496 | */ | |
497 | mask = readl(&host->reg->norintstsen); | |
498 | mask &= ~(0xffff); | |
8e42f0d6 A |
499 | mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | |
500 | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | | |
5a762e25 | 501 | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | |
8e42f0d6 A |
502 | TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | |
503 | TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); | |
21ef6a10 TW |
504 | writel(mask, &host->reg->norintstsen); |
505 | ||
506 | /* | |
507 | * NORMAL Interrupt Signal Enable Register init | |
508 | * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable | |
509 | */ | |
510 | mask = readl(&host->reg->norintsigen); | |
511 | mask &= ~(0xffff); | |
8e42f0d6 | 512 | mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; |
21ef6a10 TW |
513 | writel(mask, &host->reg->norintsigen); |
514 | ||
515 | return 0; | |
516 | } | |
517 | ||
19d7bf3d | 518 | static int tegra_mmc_getcd(struct mmc *mmc) |
bf83662b | 519 | { |
93bfd616 | 520 | struct mmc_host *host = mmc->priv; |
bf83662b | 521 | |
29f3e3f2 | 522 | debug("tegra_mmc_getcd called\n"); |
bf83662b | 523 | |
0347960b SG |
524 | if (dm_gpio_is_valid(&host->cd_gpio)) |
525 | return dm_gpio_get_value(&host->cd_gpio); | |
bf83662b TR |
526 | |
527 | return 1; | |
528 | } | |
529 | ||
ab769f22 PA |
530 | static const struct mmc_ops tegra_mmc_ops = { |
531 | .send_cmd = tegra_mmc_send_cmd, | |
532 | .set_ios = tegra_mmc_set_ios, | |
533 | .init = tegra_mmc_core_init, | |
534 | .getcd = tegra_mmc_getcd, | |
535 | }; | |
536 | ||
707ac1ad | 537 | static int do_mmc_init(int dev_index, bool removable) |
21ef6a10 | 538 | { |
de71fbe4 | 539 | struct mmc_host *host; |
21ef6a10 TW |
540 | struct mmc *mmc; |
541 | ||
c9aa831e | 542 | /* DT should have been read & host config filled in */ |
de71fbe4 | 543 | host = &mmc_host[dev_index]; |
c9aa831e TW |
544 | if (!host->enabled) |
545 | return -1; | |
de71fbe4 | 546 | |
0347960b SG |
547 | debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", |
548 | dev_index, host->width, gpio_get_number(&host->pwr_gpio), | |
549 | gpio_get_number(&host->cd_gpio)); | |
de71fbe4 | 550 | |
c9aa831e | 551 | host->clock = 0; |
39f63332 | 552 | #ifndef CONFIG_TEGRA186 |
de71fbe4 | 553 | clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); |
39f63332 | 554 | #endif |
de71fbe4 | 555 | |
0347960b SG |
556 | if (dm_gpio_is_valid(&host->pwr_gpio)) |
557 | dm_gpio_set_value(&host->pwr_gpio, 1); | |
9877841f | 558 | |
93bfd616 | 559 | memset(&host->cfg, 0, sizeof(host->cfg)); |
21ef6a10 | 560 | |
93bfd616 PA |
561 | host->cfg.name = "Tegra SD/MMC"; |
562 | host->cfg.ops = &tegra_mmc_ops; | |
21ef6a10 | 563 | |
93bfd616 PA |
564 | host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; |
565 | host->cfg.host_caps = 0; | |
c9aa831e | 566 | if (host->width == 8) |
93bfd616 | 567 | host->cfg.host_caps |= MMC_MODE_8BIT; |
c9aa831e | 568 | if (host->width >= 4) |
93bfd616 | 569 | host->cfg.host_caps |= MMC_MODE_4BIT; |
5a20397b | 570 | host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; |
21ef6a10 TW |
571 | |
572 | /* | |
573 | * min freq is for card identification, and is the highest | |
574 | * low-speed SDIO card frequency (actually 400KHz) | |
575 | * max freq is highest HS eMMC clock as per the SD/MMC spec | |
576 | * (actually 52MHz) | |
21ef6a10 | 577 | */ |
93bfd616 | 578 | host->cfg.f_min = 375000; |
39f63332 | 579 | #ifndef CONFIG_TEGRA186 |
93bfd616 | 580 | host->cfg.f_max = 48000000; |
39f63332 SW |
581 | #else |
582 | host->cfg.f_max = 375000; | |
583 | #endif | |
21ef6a10 | 584 | |
93bfd616 PA |
585 | host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; |
586 | ||
587 | mmc = mmc_create(&host->cfg, host); | |
707ac1ad | 588 | mmc->block_dev.removable = removable; |
93bfd616 PA |
589 | if (mmc == NULL) |
590 | return -1; | |
21ef6a10 TW |
591 | |
592 | return 0; | |
593 | } | |
c9aa831e TW |
594 | |
595 | /** | |
596 | * Get the host address and peripheral ID for a node. | |
597 | * | |
598 | * @param blob fdt blob | |
599 | * @param node Device index (0-3) | |
600 | * @param host Structure to fill in (reg, width, mmc_id) | |
601 | */ | |
707ac1ad SG |
602 | static int mmc_get_config(const void *blob, int node, struct mmc_host *host, |
603 | bool *removablep) | |
c9aa831e TW |
604 | { |
605 | debug("%s: node = %d\n", __func__, node); | |
606 | ||
607 | host->enabled = fdtdec_get_is_enabled(blob, node); | |
608 | ||
609 | host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); | |
610 | if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { | |
611 | debug("%s: no sdmmc base reg info found\n", __func__); | |
612 | return -FDT_ERR_NOTFOUND; | |
613 | } | |
614 | ||
39f63332 | 615 | #ifndef CONFIG_TEGRA186 |
c9aa831e TW |
616 | host->mmc_id = clock_decode_periph_id(blob, node); |
617 | if (host->mmc_id == PERIPH_ID_NONE) { | |
618 | debug("%s: could not decode periph id\n", __func__); | |
619 | return -FDT_ERR_NOTFOUND; | |
620 | } | |
39f63332 | 621 | #endif |
c9aa831e TW |
622 | |
623 | /* | |
624 | * NOTE: mmc->bus_width is determined by mmc.c dynamically. | |
625 | * TBD: Override it with this value? | |
626 | */ | |
627 | host->width = fdtdec_get_int(blob, node, "bus-width", 0); | |
628 | if (!host->width) | |
629 | debug("%s: no sdmmc width found\n", __func__); | |
630 | ||
631 | /* These GPIOs are optional */ | |
0347960b SG |
632 | gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, |
633 | GPIOD_IS_IN); | |
634 | gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, | |
635 | GPIOD_IS_IN); | |
636 | gpio_request_by_name_nodev(blob, node, "power-gpios", 0, | |
637 | &host->pwr_gpio, GPIOD_IS_OUT); | |
707ac1ad | 638 | *removablep = !fdtdec_get_bool(blob, node, "non-removable"); |
c9aa831e TW |
639 | |
640 | debug("%s: found controller at %p, width = %d, periph_id = %d\n", | |
39f63332 SW |
641 | __func__, host->reg, host->width, |
642 | #ifndef CONFIG_TEGRA186 | |
643 | host->mmc_id | |
644 | #else | |
645 | -1 | |
646 | #endif | |
647 | ); | |
c9aa831e TW |
648 | return 0; |
649 | } | |
650 | ||
651 | /* | |
652 | * Process a list of nodes, adding them to our list of SDMMC ports. | |
653 | * | |
654 | * @param blob fdt blob | |
655 | * @param node_list list of nodes to process (any <=0 are ignored) | |
656 | * @param count number of nodes to process | |
657 | * @return 0 if ok, -1 on error | |
658 | */ | |
659 | static int process_nodes(const void *blob, int node_list[], int count) | |
660 | { | |
661 | struct mmc_host *host; | |
707ac1ad | 662 | bool removable; |
c9aa831e TW |
663 | int i, node; |
664 | ||
665 | debug("%s: count = %d\n", __func__, count); | |
666 | ||
667 | /* build mmc_host[] for each controller */ | |
668 | for (i = 0; i < count; i++) { | |
669 | node = node_list[i]; | |
670 | if (node <= 0) | |
671 | continue; | |
672 | ||
673 | host = &mmc_host[i]; | |
674 | host->id = i; | |
675 | ||
707ac1ad | 676 | if (mmc_get_config(blob, node, host, &removable)) { |
c9aa831e TW |
677 | printf("%s: failed to decode dev %d\n", __func__, i); |
678 | return -1; | |
679 | } | |
707ac1ad | 680 | do_mmc_init(i, removable); |
c9aa831e TW |
681 | } |
682 | return 0; | |
683 | } | |
684 | ||
685 | void tegra_mmc_init(void) | |
686 | { | |
f175603f | 687 | int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; |
c9aa831e TW |
688 | const void *blob = gd->fdt_blob; |
689 | debug("%s entry\n", __func__); | |
690 | ||
39f63332 SW |
691 | /* See if any Tegra186 MMC controllers are present */ |
692 | count = fdtdec_find_aliases_for_id(blob, "sdhci", | |
693 | COMPAT_NVIDIA_TEGRA186_SDMMC, node_list, | |
694 | CONFIG_SYS_MMC_MAX_DEVICE); | |
695 | debug("%s: count of Tegra186 sdhci nodes is %d\n", __func__, count); | |
696 | if (process_nodes(blob, node_list, count)) { | |
697 | printf("%s: Error processing T186 mmc node(s)!\n", __func__); | |
698 | return; | |
699 | } | |
700 | ||
7aaa5a60 TW |
701 | /* See if any Tegra210 MMC controllers are present */ |
702 | count = fdtdec_find_aliases_for_id(blob, "sdhci", | |
703 | COMPAT_NVIDIA_TEGRA210_SDMMC, node_list, | |
704 | CONFIG_SYS_MMC_MAX_DEVICE); | |
705 | debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count); | |
706 | if (process_nodes(blob, node_list, count)) { | |
e05ab0da | 707 | printf("%s: Error processing T210 mmc node(s)!\n", __func__); |
7aaa5a60 TW |
708 | return; |
709 | } | |
710 | ||
a73ca478 SW |
711 | /* See if any Tegra124 MMC controllers are present */ |
712 | count = fdtdec_find_aliases_for_id(blob, "sdhci", | |
f175603f SW |
713 | COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, |
714 | CONFIG_SYS_MMC_MAX_DEVICE); | |
a73ca478 SW |
715 | debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); |
716 | if (process_nodes(blob, node_list, count)) { | |
e05ab0da | 717 | printf("%s: Error processing T124 mmc node(s)!\n", __func__); |
a73ca478 SW |
718 | return; |
719 | } | |
720 | ||
2d348a16 | 721 | /* See if any Tegra30 MMC controllers are present */ |
c9aa831e | 722 | count = fdtdec_find_aliases_for_id(blob, "sdhci", |
f175603f SW |
723 | COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, |
724 | CONFIG_SYS_MMC_MAX_DEVICE); | |
2d348a16 TW |
725 | debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); |
726 | if (process_nodes(blob, node_list, count)) { | |
727 | printf("%s: Error processing T30 mmc node(s)!\n", __func__); | |
728 | return; | |
729 | } | |
c9aa831e | 730 | |
2d348a16 TW |
731 | /* Now look for any Tegra20 MMC controllers */ |
732 | count = fdtdec_find_aliases_for_id(blob, "sdhci", | |
f175603f SW |
733 | COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, |
734 | CONFIG_SYS_MMC_MAX_DEVICE); | |
2d348a16 | 735 | debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); |
c9aa831e | 736 | if (process_nodes(blob, node_list, count)) { |
2d348a16 | 737 | printf("%s: Error processing T20 mmc node(s)!\n", __func__); |
c9aa831e TW |
738 | return; |
739 | } | |
740 | } |