]>
Commit | Line | Data |
---|---|---|
21ef6a10 TW |
1 | /* |
2 | * (C) Copyright 2009 SAMSUNG Electronics | |
3 | * Minkyu Kang <[email protected]> | |
4 | * Jaehoon Chung <[email protected]> | |
3f82d89d | 5 | * Portions Copyright 2011-2012 NVIDIA Corporation |
21ef6a10 TW |
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 as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | */ | |
21 | ||
22 | #include <common.h> | |
23 | #include <mmc.h> | |
9877841f | 24 | #include <asm/gpio.h> |
21ef6a10 TW |
25 | #include <asm/io.h> |
26 | #include <asm/arch/clk_rst.h> | |
4ed59e70 | 27 | #include <asm/arch/clock.h> |
29f3e3f2 | 28 | #include <asm/arch/tegra_mmc.h> |
21ef6a10 TW |
29 | |
30 | /* support 4 mmc hosts */ | |
31 | struct mmc mmc_dev[4]; | |
32 | struct mmc_host mmc_host[4]; | |
33 | ||
4ed59e70 SG |
34 | |
35 | /** | |
36 | * Get the host address and peripheral ID for a device. Devices are numbered | |
37 | * from 0 to 3. | |
38 | * | |
39 | * @param host Structure to fill in (base, reg, mmc_id) | |
40 | * @param dev_index Device index (0-3) | |
41 | */ | |
29f3e3f2 | 42 | static void tegra_get_setup(struct mmc_host *host, int dev_index) |
21ef6a10 | 43 | { |
29f3e3f2 | 44 | debug("tegra_get_setup: dev_index = %d\n", dev_index); |
21ef6a10 TW |
45 | |
46 | switch (dev_index) { | |
21ef6a10 | 47 | case 1: |
29f3e3f2 | 48 | host->base = TEGRA_SDMMC3_BASE; |
4ed59e70 | 49 | host->mmc_id = PERIPH_ID_SDMMC3; |
21ef6a10 TW |
50 | break; |
51 | case 2: | |
29f3e3f2 | 52 | host->base = TEGRA_SDMMC2_BASE; |
4ed59e70 | 53 | host->mmc_id = PERIPH_ID_SDMMC2; |
21ef6a10 TW |
54 | break; |
55 | case 3: | |
29f3e3f2 | 56 | host->base = TEGRA_SDMMC1_BASE; |
4ed59e70 | 57 | host->mmc_id = PERIPH_ID_SDMMC1; |
21ef6a10 | 58 | break; |
4ed59e70 | 59 | case 0: |
21ef6a10 | 60 | default: |
29f3e3f2 | 61 | host->base = TEGRA_SDMMC4_BASE; |
4ed59e70 | 62 | host->mmc_id = PERIPH_ID_SDMMC4; |
21ef6a10 TW |
63 | break; |
64 | } | |
65 | ||
29f3e3f2 | 66 | host->reg = (struct tegra_mmc *)host->base; |
21ef6a10 TW |
67 | } |
68 | ||
69 | static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) | |
70 | { | |
71 | unsigned char ctrl; | |
72 | ||
73 | debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n", | |
74 | (u32)data->dest, data->blocks, data->blocksize); | |
75 | ||
76 | writel((u32)data->dest, &host->reg->sysad); | |
77 | /* | |
78 | * DMASEL[4:3] | |
79 | * 00 = Selects SDMA | |
80 | * 01 = Reserved | |
81 | * 10 = Selects 32-bit Address ADMA2 | |
82 | * 11 = Selects 64-bit Address ADMA2 | |
83 | */ | |
84 | ctrl = readb(&host->reg->hostctl); | |
8e42f0d6 A |
85 | ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; |
86 | ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; | |
21ef6a10 TW |
87 | writeb(ctrl, &host->reg->hostctl); |
88 | ||
89 | /* We do not handle DMA boundaries, so set it to max (512 KiB) */ | |
90 | writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); | |
91 | writew(data->blocks, &host->reg->blkcnt); | |
92 | } | |
93 | ||
94 | static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) | |
95 | { | |
96 | unsigned short mode; | |
97 | debug(" mmc_set_transfer_mode called\n"); | |
98 | /* | |
99 | * TRNMOD | |
100 | * MUL1SIN0[5] : Multi/Single Block Select | |
101 | * RD1WT0[4] : Data Transfer Direction Select | |
102 | * 1 = read | |
103 | * 0 = write | |
104 | * ENACMD12[2] : Auto CMD12 Enable | |
105 | * ENBLKCNT[1] : Block Count Enable | |
106 | * ENDMA[0] : DMA Enable | |
107 | */ | |
8e42f0d6 A |
108 | mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | |
109 | TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); | |
110 | ||
21ef6a10 | 111 | if (data->blocks > 1) |
8e42f0d6 A |
112 | mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; |
113 | ||
21ef6a10 | 114 | if (data->flags & MMC_DATA_READ) |
8e42f0d6 | 115 | mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; |
21ef6a10 | 116 | |
98450d02 SG |
117 | if (data->flags & MMC_DATA_WRITE) { |
118 | if ((uintptr_t)data->src & (ARCH_DMA_MINALIGN - 1)) | |
119 | printf("Warning: unaligned write to %p may fail\n", | |
120 | data->src); | |
121 | flush_dcache_range((ulong)data->src, (ulong)data->src + | |
122 | data->blocks * data->blocksize); | |
123 | } | |
124 | ||
21ef6a10 TW |
125 | writew(mode, &host->reg->trnmod); |
126 | } | |
127 | ||
0963ff3a A |
128 | static int mmc_wait_inhibit(struct mmc_host *host, |
129 | struct mmc_cmd *cmd, | |
130 | struct mmc_data *data, | |
131 | unsigned int timeout) | |
21ef6a10 | 132 | { |
21ef6a10 TW |
133 | /* |
134 | * PRNSTS | |
0963ff3a A |
135 | * CMDINHDAT[1] : Command Inhibit (DAT) |
136 | * CMDINHCMD[0] : Command Inhibit (CMD) | |
21ef6a10 | 137 | */ |
0963ff3a | 138 | unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; |
21ef6a10 TW |
139 | |
140 | /* | |
141 | * We shouldn't wait for data inhibit for stop commands, even | |
142 | * though they might use busy signaling | |
143 | */ | |
0963ff3a A |
144 | if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) |
145 | mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; | |
21ef6a10 TW |
146 | |
147 | while (readl(&host->reg->prnsts) & mask) { | |
148 | if (timeout == 0) { | |
149 | printf("%s: timeout error\n", __func__); | |
150 | return -1; | |
151 | } | |
152 | timeout--; | |
153 | udelay(1000); | |
154 | } | |
155 | ||
0963ff3a A |
156 | return 0; |
157 | } | |
158 | ||
159 | static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, | |
160 | struct mmc_data *data) | |
161 | { | |
162 | struct mmc_host *host = (struct mmc_host *)mmc->priv; | |
163 | int flags, i; | |
164 | int result; | |
60e242ed | 165 | unsigned int mask = 0; |
0963ff3a A |
166 | unsigned int retry = 0x100000; |
167 | debug(" mmc_send_cmd called\n"); | |
168 | ||
169 | result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); | |
170 | ||
171 | if (result < 0) | |
172 | return result; | |
173 | ||
21ef6a10 TW |
174 | if (data) |
175 | mmc_prepare_data(host, data); | |
176 | ||
177 | debug("cmd->arg: %08x\n", cmd->cmdarg); | |
178 | writel(cmd->cmdarg, &host->reg->argument); | |
179 | ||
180 | if (data) | |
181 | mmc_set_transfer_mode(host, data); | |
182 | ||
183 | if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) | |
184 | return -1; | |
185 | ||
186 | /* | |
187 | * CMDREG | |
188 | * CMDIDX[13:8] : Command index | |
189 | * DATAPRNT[5] : Data Present Select | |
190 | * ENCMDIDX[4] : Command Index Check Enable | |
191 | * ENCMDCRC[3] : Command CRC Check Enable | |
192 | * RSPTYP[1:0] | |
193 | * 00 = No Response | |
194 | * 01 = Length 136 | |
195 | * 10 = Length 48 | |
196 | * 11 = Length 48 Check busy after response | |
197 | */ | |
198 | if (!(cmd->resp_type & MMC_RSP_PRESENT)) | |
8e42f0d6 | 199 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; |
21ef6a10 | 200 | else if (cmd->resp_type & MMC_RSP_136) |
8e42f0d6 | 201 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; |
21ef6a10 | 202 | else if (cmd->resp_type & MMC_RSP_BUSY) |
8e42f0d6 | 203 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; |
21ef6a10 | 204 | else |
8e42f0d6 | 205 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; |
21ef6a10 TW |
206 | |
207 | if (cmd->resp_type & MMC_RSP_CRC) | |
8e42f0d6 | 208 | flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; |
21ef6a10 | 209 | if (cmd->resp_type & MMC_RSP_OPCODE) |
8e42f0d6 | 210 | flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; |
21ef6a10 | 211 | if (data) |
8e42f0d6 | 212 | flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; |
21ef6a10 TW |
213 | |
214 | debug("cmd: %d\n", cmd->cmdidx); | |
215 | ||
216 | writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); | |
217 | ||
218 | for (i = 0; i < retry; i++) { | |
219 | mask = readl(&host->reg->norintsts); | |
220 | /* Command Complete */ | |
8e42f0d6 | 221 | if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { |
21ef6a10 TW |
222 | if (!data) |
223 | writel(mask, &host->reg->norintsts); | |
224 | break; | |
225 | } | |
226 | } | |
227 | ||
228 | if (i == retry) { | |
229 | printf("%s: waiting for status update\n", __func__); | |
cf39cf55 | 230 | writel(mask, &host->reg->norintsts); |
21ef6a10 TW |
231 | return TIMEOUT; |
232 | } | |
233 | ||
8e42f0d6 | 234 | if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { |
21ef6a10 TW |
235 | /* Timeout Error */ |
236 | debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); | |
cf39cf55 | 237 | writel(mask, &host->reg->norintsts); |
21ef6a10 | 238 | return TIMEOUT; |
8e42f0d6 | 239 | } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 TW |
240 | /* Error Interrupt */ |
241 | debug("error: %08x cmd %d\n", mask, cmd->cmdidx); | |
cf39cf55 | 242 | writel(mask, &host->reg->norintsts); |
21ef6a10 TW |
243 | return -1; |
244 | } | |
245 | ||
246 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
247 | if (cmd->resp_type & MMC_RSP_136) { | |
248 | /* CRC is stripped so we need to do some shifting. */ | |
249 | for (i = 0; i < 4; i++) { | |
250 | unsigned int offset = | |
251 | (unsigned int)(&host->reg->rspreg3 - i); | |
252 | cmd->response[i] = readl(offset) << 8; | |
253 | ||
254 | if (i != 3) { | |
255 | cmd->response[i] |= | |
256 | readb(offset - 1); | |
257 | } | |
258 | debug("cmd->resp[%d]: %08x\n", | |
259 | i, cmd->response[i]); | |
260 | } | |
261 | } else if (cmd->resp_type & MMC_RSP_BUSY) { | |
262 | for (i = 0; i < retry; i++) { | |
263 | /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ | |
264 | if (readl(&host->reg->prnsts) | |
265 | & (1 << 20)) /* DAT[0] */ | |
266 | break; | |
267 | } | |
268 | ||
269 | if (i == retry) { | |
270 | printf("%s: card is still busy\n", __func__); | |
cf39cf55 | 271 | writel(mask, &host->reg->norintsts); |
21ef6a10 TW |
272 | return TIMEOUT; |
273 | } | |
274 | ||
275 | cmd->response[0] = readl(&host->reg->rspreg0); | |
276 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); | |
277 | } else { | |
278 | cmd->response[0] = readl(&host->reg->rspreg0); | |
279 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); | |
280 | } | |
281 | } | |
282 | ||
283 | if (data) { | |
9b3d1873 A |
284 | unsigned long start = get_timer(0); |
285 | ||
21ef6a10 TW |
286 | while (1) { |
287 | mask = readl(&host->reg->norintsts); | |
288 | ||
8e42f0d6 | 289 | if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 TW |
290 | /* Error Interrupt */ |
291 | writel(mask, &host->reg->norintsts); | |
292 | printf("%s: error during transfer: 0x%08x\n", | |
293 | __func__, mask); | |
294 | return -1; | |
8e42f0d6 | 295 | } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { |
5a762e25 A |
296 | /* |
297 | * DMA Interrupt, restart the transfer where | |
298 | * it was interrupted. | |
299 | */ | |
300 | unsigned int address = readl(&host->reg->sysad); | |
301 | ||
21ef6a10 | 302 | debug("DMA end\n"); |
5a762e25 A |
303 | writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, |
304 | &host->reg->norintsts); | |
305 | writel(address, &host->reg->sysad); | |
8e42f0d6 | 306 | } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { |
21ef6a10 TW |
307 | /* Transfer Complete */ |
308 | debug("r/w is done\n"); | |
309 | break; | |
9b3d1873 A |
310 | } else if (get_timer(start) > 2000UL) { |
311 | writel(mask, &host->reg->norintsts); | |
312 | printf("%s: MMC Timeout\n" | |
313 | " Interrupt status 0x%08x\n" | |
314 | " Interrupt status enable 0x%08x\n" | |
315 | " Interrupt signal enable 0x%08x\n" | |
316 | " Present status 0x%08x\n", | |
317 | __func__, mask, | |
318 | readl(&host->reg->norintstsen), | |
319 | readl(&host->reg->norintsigen), | |
320 | readl(&host->reg->prnsts)); | |
321 | return -1; | |
21ef6a10 TW |
322 | } |
323 | } | |
324 | writel(mask, &host->reg->norintsts); | |
98450d02 SG |
325 | if (data->flags & MMC_DATA_READ) { |
326 | if ((uintptr_t)data->dest & (ARCH_DMA_MINALIGN - 1)) | |
327 | printf("Warning: unaligned read from %p " | |
328 | "may fail\n", data->dest); | |
329 | invalidate_dcache_range((ulong)data->dest, | |
330 | (ulong)data->dest + | |
331 | data->blocks * data->blocksize); | |
332 | } | |
21ef6a10 TW |
333 | } |
334 | ||
335 | udelay(1000); | |
336 | return 0; | |
337 | } | |
338 | ||
339 | static void mmc_change_clock(struct mmc_host *host, uint clock) | |
340 | { | |
4ed59e70 | 341 | int div; |
21ef6a10 TW |
342 | unsigned short clk; |
343 | unsigned long timeout; | |
4ed59e70 | 344 | |
21ef6a10 TW |
345 | debug(" mmc_change_clock called\n"); |
346 | ||
4ed59e70 | 347 | /* |
29f3e3f2 | 348 | * Change Tegra SDMMCx clock divisor here. Source is 216MHz, |
4ed59e70 SG |
349 | * PLLP_OUT0 |
350 | */ | |
21ef6a10 TW |
351 | if (clock == 0) |
352 | goto out; | |
4ed59e70 SG |
353 | clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, |
354 | &div); | |
355 | debug("div = %d\n", div); | |
21ef6a10 TW |
356 | |
357 | writew(0, &host->reg->clkcon); | |
358 | ||
21ef6a10 TW |
359 | /* |
360 | * CLKCON | |
361 | * SELFREQ[15:8] : base clock divided by value | |
362 | * ENSDCLK[2] : SD Clock Enable | |
363 | * STBLINTCLK[1] : Internal Clock Stable | |
364 | * ENINTCLK[0] : Internal Clock Enable | |
365 | */ | |
4ed59e70 | 366 | div >>= 1; |
8e42f0d6 A |
367 | clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | |
368 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); | |
21ef6a10 TW |
369 | writew(clk, &host->reg->clkcon); |
370 | ||
371 | /* Wait max 10 ms */ | |
372 | timeout = 10; | |
8e42f0d6 A |
373 | while (!(readw(&host->reg->clkcon) & |
374 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { | |
21ef6a10 TW |
375 | if (timeout == 0) { |
376 | printf("%s: timeout error\n", __func__); | |
377 | return; | |
378 | } | |
379 | timeout--; | |
380 | udelay(1000); | |
381 | } | |
382 | ||
8e42f0d6 | 383 | clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; |
21ef6a10 TW |
384 | writew(clk, &host->reg->clkcon); |
385 | ||
386 | debug("mmc_change_clock: clkcon = %08X\n", clk); | |
21ef6a10 TW |
387 | |
388 | out: | |
389 | host->clock = clock; | |
390 | } | |
391 | ||
392 | static void mmc_set_ios(struct mmc *mmc) | |
393 | { | |
394 | struct mmc_host *host = mmc->priv; | |
395 | unsigned char ctrl; | |
396 | debug(" mmc_set_ios called\n"); | |
397 | ||
398 | debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); | |
399 | ||
400 | /* Change clock first */ | |
21ef6a10 TW |
401 | mmc_change_clock(host, mmc->clock); |
402 | ||
403 | ctrl = readb(&host->reg->hostctl); | |
404 | ||
405 | /* | |
406 | * WIDE8[5] | |
407 | * 0 = Depend on WIDE4 | |
408 | * 1 = 8-bit mode | |
409 | * WIDE4[1] | |
410 | * 1 = 4-bit mode | |
411 | * 0 = 1-bit mode | |
412 | */ | |
413 | if (mmc->bus_width == 8) | |
414 | ctrl |= (1 << 5); | |
415 | else if (mmc->bus_width == 4) | |
416 | ctrl |= (1 << 1); | |
417 | else | |
418 | ctrl &= ~(1 << 1); | |
419 | ||
420 | writeb(ctrl, &host->reg->hostctl); | |
421 | debug("mmc_set_ios: hostctl = %08X\n", ctrl); | |
422 | } | |
423 | ||
424 | static void mmc_reset(struct mmc_host *host) | |
425 | { | |
426 | unsigned int timeout; | |
427 | debug(" mmc_reset called\n"); | |
428 | ||
429 | /* | |
430 | * RSTALL[0] : Software reset for all | |
431 | * 1 = reset | |
432 | * 0 = work | |
433 | */ | |
8e42f0d6 | 434 | writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); |
21ef6a10 TW |
435 | |
436 | host->clock = 0; | |
437 | ||
438 | /* Wait max 100 ms */ | |
439 | timeout = 100; | |
440 | ||
441 | /* hw clears the bit when it's done */ | |
8e42f0d6 | 442 | while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { |
21ef6a10 TW |
443 | if (timeout == 0) { |
444 | printf("%s: timeout error\n", __func__); | |
445 | return; | |
446 | } | |
447 | timeout--; | |
448 | udelay(1000); | |
449 | } | |
450 | } | |
451 | ||
452 | static int mmc_core_init(struct mmc *mmc) | |
453 | { | |
454 | struct mmc_host *host = (struct mmc_host *)mmc->priv; | |
455 | unsigned int mask; | |
456 | debug(" mmc_core_init called\n"); | |
457 | ||
458 | mmc_reset(host); | |
459 | ||
460 | host->version = readw(&host->reg->hcver); | |
461 | debug("host version = %x\n", host->version); | |
462 | ||
463 | /* mask all */ | |
464 | writel(0xffffffff, &host->reg->norintstsen); | |
465 | writel(0xffffffff, &host->reg->norintsigen); | |
466 | ||
467 | writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ | |
468 | /* | |
469 | * NORMAL Interrupt Status Enable Register init | |
470 | * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable | |
471 | * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable | |
5a762e25 | 472 | * [3] ENSTADMAINT : DMA boundary interrupt |
21ef6a10 TW |
473 | * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable |
474 | * [0] ENSTACMDCMPLT : Command Complete Status Enable | |
475 | */ | |
476 | mask = readl(&host->reg->norintstsen); | |
477 | mask &= ~(0xffff); | |
8e42f0d6 A |
478 | mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | |
479 | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | | |
5a762e25 | 480 | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | |
8e42f0d6 A |
481 | TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | |
482 | TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); | |
21ef6a10 TW |
483 | writel(mask, &host->reg->norintstsen); |
484 | ||
485 | /* | |
486 | * NORMAL Interrupt Signal Enable Register init | |
487 | * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable | |
488 | */ | |
489 | mask = readl(&host->reg->norintsigen); | |
490 | mask &= ~(0xffff); | |
8e42f0d6 | 491 | mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; |
21ef6a10 TW |
492 | writel(mask, &host->reg->norintsigen); |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
29f3e3f2 | 497 | int tegra_mmc_getcd(struct mmc *mmc) |
bf83662b TR |
498 | { |
499 | struct mmc_host *host = (struct mmc_host *)mmc->priv; | |
500 | ||
29f3e3f2 | 501 | debug("tegra_mmc_getcd called\n"); |
bf83662b TR |
502 | |
503 | if (host->cd_gpio >= 0) | |
504 | return !gpio_get_value(host->cd_gpio); | |
505 | ||
506 | return 1; | |
507 | } | |
508 | ||
29f3e3f2 | 509 | int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) |
21ef6a10 | 510 | { |
de71fbe4 | 511 | struct mmc_host *host; |
9877841f | 512 | char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ |
21ef6a10 TW |
513 | struct mmc *mmc; |
514 | ||
29f3e3f2 | 515 | debug(" tegra_mmc_init: index %d, bus width %d " |
9877841f SW |
516 | "pwr_gpio %d cd_gpio %d\n", |
517 | dev_index, bus_width, pwr_gpio, cd_gpio); | |
21ef6a10 | 518 | |
de71fbe4 SW |
519 | host = &mmc_host[dev_index]; |
520 | ||
521 | host->clock = 0; | |
9877841f SW |
522 | host->pwr_gpio = pwr_gpio; |
523 | host->cd_gpio = cd_gpio; | |
29f3e3f2 | 524 | tegra_get_setup(host, dev_index); |
de71fbe4 SW |
525 | |
526 | clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); | |
527 | ||
9877841f SW |
528 | if (host->pwr_gpio >= 0) { |
529 | sprintf(gpusage, "SD/MMC%d PWR", dev_index); | |
530 | gpio_request(host->pwr_gpio, gpusage); | |
531 | gpio_direction_output(host->pwr_gpio, 1); | |
532 | } | |
533 | ||
534 | if (host->cd_gpio >= 0) { | |
535 | sprintf(gpusage, "SD/MMC%d CD", dev_index); | |
536 | gpio_request(host->cd_gpio, gpusage); | |
537 | gpio_direction_input(host->cd_gpio); | |
538 | } | |
539 | ||
21ef6a10 TW |
540 | mmc = &mmc_dev[dev_index]; |
541 | ||
29f3e3f2 | 542 | sprintf(mmc->name, "Tegra SD/MMC"); |
de71fbe4 | 543 | mmc->priv = host; |
21ef6a10 TW |
544 | mmc->send_cmd = mmc_send_cmd; |
545 | mmc->set_ios = mmc_set_ios; | |
546 | mmc->init = mmc_core_init; | |
29f3e3f2 | 547 | mmc->getcd = tegra_mmc_getcd; |
21ef6a10 TW |
548 | |
549 | mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; | |
550 | if (bus_width == 8) | |
551 | mmc->host_caps = MMC_MODE_8BIT; | |
552 | else | |
553 | mmc->host_caps = MMC_MODE_4BIT; | |
ccf7988b | 554 | mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; |
21ef6a10 TW |
555 | |
556 | /* | |
557 | * min freq is for card identification, and is the highest | |
558 | * low-speed SDIO card frequency (actually 400KHz) | |
559 | * max freq is highest HS eMMC clock as per the SD/MMC spec | |
560 | * (actually 52MHz) | |
561 | * Both of these are the closest equivalents w/216MHz source | |
29f3e3f2 | 562 | * clock and Tegra SDMMC divisors. |
21ef6a10 TW |
563 | */ |
564 | mmc->f_min = 375000; | |
565 | mmc->f_max = 48000000; | |
566 | ||
21ef6a10 TW |
567 | mmc_register(mmc); |
568 | ||
569 | return 0; | |
570 | } |