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