]>
Commit | Line | Data |
---|---|---|
72d42bad NI |
1 | /* |
2 | * drivers/mmc/sh_sdhi.c | |
3 | * | |
4 | * SD/MMC driver for Renesas rmobile ARM SoCs. | |
5 | * | |
6 | * Copyright (C) 2011,2013-2014 Renesas Electronics Corporation | |
7 | * Copyright (C) 2014 Nobuhiro Iwamatsu <[email protected]> | |
8 | * Copyright (C) 2008-2009 Renesas Solutions Corp. | |
9 | * | |
10 | * SPDX-License-Identifier: GPL-2.0 | |
11 | */ | |
12 | ||
13 | #include <common.h> | |
14 | #include <malloc.h> | |
15 | #include <mmc.h> | |
16 | #include <asm/errno.h> | |
17 | #include <asm/io.h> | |
18 | #include <asm/arch/rmobile.h> | |
19 | #include <asm/arch/sh_sdhi.h> | |
20 | ||
21 | #define DRIVER_NAME "sh-sdhi" | |
22 | ||
23 | struct sh_sdhi_host { | |
24 | unsigned long addr; | |
25 | int ch; | |
26 | int bus_shift; | |
27 | unsigned long quirks; | |
28 | unsigned char wait_int; | |
29 | unsigned char sd_error; | |
30 | unsigned char detect_waiting; | |
31 | }; | |
32 | static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val) | |
33 | { | |
34 | writew(val, host->addr + (reg << host->bus_shift)); | |
35 | } | |
36 | ||
37 | static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg) | |
38 | { | |
39 | return readw(host->addr + (reg << host->bus_shift)); | |
40 | } | |
41 | ||
42 | static void *mmc_priv(struct mmc *mmc) | |
43 | { | |
44 | return (void *)mmc->priv; | |
45 | } | |
46 | ||
47 | static void sh_sdhi_detect(struct sh_sdhi_host *host) | |
48 | { | |
49 | sh_sdhi_writew(host, SDHI_OPTION, | |
50 | OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION)); | |
51 | ||
52 | host->detect_waiting = 0; | |
53 | } | |
54 | ||
55 | static int sh_sdhi_intr(void *dev_id) | |
56 | { | |
57 | struct sh_sdhi_host *host = dev_id; | |
58 | int state1 = 0, state2 = 0; | |
59 | ||
60 | state1 = sh_sdhi_readw(host, SDHI_INFO1); | |
61 | state2 = sh_sdhi_readw(host, SDHI_INFO2); | |
62 | ||
63 | debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2); | |
64 | ||
65 | /* CARD Insert */ | |
66 | if (state1 & INFO1_CARD_IN) { | |
67 | sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN); | |
68 | if (!host->detect_waiting) { | |
69 | host->detect_waiting = 1; | |
70 | sh_sdhi_detect(host); | |
71 | } | |
72 | sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | | |
73 | INFO1M_ACCESS_END | INFO1M_CARD_IN | | |
74 | INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); | |
75 | return -EAGAIN; | |
76 | } | |
77 | /* CARD Removal */ | |
78 | if (state1 & INFO1_CARD_RE) { | |
79 | sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE); | |
80 | if (!host->detect_waiting) { | |
81 | host->detect_waiting = 1; | |
82 | sh_sdhi_detect(host); | |
83 | } | |
84 | sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | | |
85 | INFO1M_ACCESS_END | INFO1M_CARD_RE | | |
86 | INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); | |
87 | sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON); | |
88 | sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF); | |
89 | return -EAGAIN; | |
90 | } | |
91 | ||
92 | if (state2 & INFO2_ALL_ERR) { | |
93 | sh_sdhi_writew(host, SDHI_INFO2, | |
94 | (unsigned short)~(INFO2_ALL_ERR)); | |
95 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
96 | INFO2M_ALL_ERR | | |
97 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
98 | host->sd_error = 1; | |
99 | host->wait_int = 1; | |
100 | return 0; | |
101 | } | |
102 | /* Respons End */ | |
103 | if (state1 & INFO1_RESP_END) { | |
104 | sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END); | |
105 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
106 | INFO1M_RESP_END | | |
107 | sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
108 | host->wait_int = 1; | |
109 | return 0; | |
110 | } | |
111 | /* SD_BUF Read Enable */ | |
112 | if (state2 & INFO2_BRE_ENABLE) { | |
113 | sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE); | |
114 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
115 | INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ | | |
116 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
117 | host->wait_int = 1; | |
118 | return 0; | |
119 | } | |
120 | /* SD_BUF Write Enable */ | |
121 | if (state2 & INFO2_BWE_ENABLE) { | |
122 | sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE); | |
123 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
124 | INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE | | |
125 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
126 | host->wait_int = 1; | |
127 | return 0; | |
128 | } | |
129 | /* Access End */ | |
130 | if (state1 & INFO1_ACCESS_END) { | |
131 | sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END); | |
132 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
133 | INFO1_ACCESS_END | | |
134 | sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
135 | host->wait_int = 1; | |
136 | return 0; | |
137 | } | |
138 | return -EAGAIN; | |
139 | } | |
140 | ||
141 | static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host) | |
142 | { | |
143 | int timeout = 10000000; | |
144 | ||
145 | while (1) { | |
146 | timeout--; | |
147 | if (timeout < 0) { | |
148 | debug(DRIVER_NAME": %s timeout\n", __func__); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | if (!sh_sdhi_intr(host)) | |
153 | break; | |
154 | ||
155 | udelay(1); /* 1 usec */ | |
156 | } | |
157 | ||
158 | return 1; /* Return value: NOT 0 = complete waiting */ | |
159 | } | |
160 | ||
161 | static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk) | |
162 | { | |
163 | u32 clkdiv, i, timeout; | |
164 | ||
165 | if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) { | |
166 | printf(DRIVER_NAME": Busy state ! Cannot change the clock\n"); | |
167 | return -EBUSY; | |
168 | } | |
169 | ||
170 | sh_sdhi_writew(host, SDHI_CLK_CTRL, | |
171 | ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL)); | |
172 | ||
173 | if (clk == 0) | |
174 | return -EIO; | |
175 | ||
176 | clkdiv = 0x80; | |
177 | i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1); | |
178 | for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1)) | |
179 | i <<= 1; | |
180 | ||
181 | sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv); | |
182 | ||
183 | timeout = 100000; | |
184 | /* Waiting for SD Bus busy to be cleared */ | |
185 | while (timeout--) { | |
186 | if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000)) | |
187 | break; | |
188 | } | |
189 | ||
190 | if (timeout) | |
191 | sh_sdhi_writew(host, SDHI_CLK_CTRL, | |
192 | CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL)); | |
193 | else | |
194 | return -EBUSY; | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | static int sh_sdhi_sync_reset(struct sh_sdhi_host *host) | |
200 | { | |
201 | u32 timeout; | |
202 | sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON); | |
203 | sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF); | |
204 | sh_sdhi_writew(host, SDHI_CLK_CTRL, | |
205 | CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL)); | |
206 | ||
207 | timeout = 100000; | |
208 | while (timeout--) { | |
209 | if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY)) | |
210 | break; | |
211 | udelay(100); | |
212 | } | |
213 | ||
214 | if (!timeout) | |
215 | return -EBUSY; | |
216 | ||
217 | if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) | |
218 | sh_sdhi_writew(host, SDHI_HOST_MODE, 1); | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static int sh_sdhi_error_manage(struct sh_sdhi_host *host) | |
224 | { | |
225 | unsigned short e_state1, e_state2; | |
226 | int ret; | |
227 | ||
228 | host->sd_error = 0; | |
229 | host->wait_int = 0; | |
230 | ||
231 | e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1); | |
232 | e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2); | |
233 | if (e_state2 & ERR_STS2_SYS_ERROR) { | |
234 | if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT) | |
235 | ret = TIMEOUT; | |
236 | else | |
237 | ret = -EILSEQ; | |
238 | debug("%s: ERR_STS2 = %04x\n", | |
239 | DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2)); | |
240 | sh_sdhi_sync_reset(host); | |
241 | ||
242 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
243 | INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); | |
244 | return ret; | |
245 | } | |
246 | if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR) | |
247 | ret = -EILSEQ; | |
248 | else | |
249 | ret = TIMEOUT; | |
250 | ||
251 | debug("%s: ERR_STS1 = %04x\n", | |
252 | DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1)); | |
253 | sh_sdhi_sync_reset(host); | |
254 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
255 | INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); | |
256 | return ret; | |
257 | } | |
258 | ||
259 | static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data) | |
260 | { | |
261 | long time; | |
262 | unsigned short blocksize, i; | |
263 | unsigned short *p = (unsigned short *)data->dest; | |
264 | ||
265 | if ((unsigned long)p & 0x00000001) { | |
266 | debug(DRIVER_NAME": %s: The data pointer is unaligned.", | |
267 | __func__); | |
268 | return -EIO; | |
269 | } | |
270 | ||
271 | host->wait_int = 0; | |
272 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
273 | ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) & | |
274 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
275 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
276 | ~INFO1M_ACCESS_END & | |
277 | sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
278 | time = sh_sdhi_wait_interrupt_flag(host); | |
279 | if (time == 0 || host->sd_error != 0) | |
280 | return sh_sdhi_error_manage(host); | |
281 | ||
282 | host->wait_int = 0; | |
283 | blocksize = sh_sdhi_readw(host, SDHI_SIZE); | |
284 | for (i = 0; i < blocksize / 2; i++) | |
285 | *p++ = sh_sdhi_readw(host, SDHI_BUF0); | |
286 | ||
287 | time = sh_sdhi_wait_interrupt_flag(host); | |
288 | if (time == 0 || host->sd_error != 0) | |
289 | return sh_sdhi_error_manage(host); | |
290 | ||
291 | host->wait_int = 0; | |
292 | return 0; | |
293 | } | |
294 | ||
295 | static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data) | |
296 | { | |
297 | long time; | |
298 | unsigned short blocksize, i, sec; | |
299 | unsigned short *p = (unsigned short *)data->dest; | |
300 | ||
301 | if ((unsigned long)p & 0x00000001) { | |
302 | debug(DRIVER_NAME": %s: The data pointer is unaligned.", | |
303 | __func__); | |
304 | return -EIO; | |
305 | } | |
306 | ||
307 | debug("%s: blocks = %d, blocksize = %d\n", | |
308 | __func__, data->blocks, data->blocksize); | |
309 | ||
310 | host->wait_int = 0; | |
311 | for (sec = 0; sec < data->blocks; sec++) { | |
312 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
313 | ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) & | |
314 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
315 | ||
316 | time = sh_sdhi_wait_interrupt_flag(host); | |
317 | if (time == 0 || host->sd_error != 0) | |
318 | return sh_sdhi_error_manage(host); | |
319 | ||
320 | host->wait_int = 0; | |
321 | blocksize = sh_sdhi_readw(host, SDHI_SIZE); | |
322 | for (i = 0; i < blocksize / 2; i++) | |
323 | *p++ = sh_sdhi_readw(host, SDHI_BUF0); | |
324 | } | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
329 | static int sh_sdhi_single_write(struct sh_sdhi_host *host, | |
330 | struct mmc_data *data) | |
331 | { | |
332 | long time; | |
333 | unsigned short blocksize, i; | |
334 | const unsigned short *p = (const unsigned short *)data->src; | |
335 | ||
336 | if ((unsigned long)p & 0x00000001) { | |
337 | debug(DRIVER_NAME": %s: The data pointer is unaligned.", | |
338 | __func__); | |
339 | return -EIO; | |
340 | } | |
341 | ||
342 | debug("%s: blocks = %d, blocksize = %d\n", | |
343 | __func__, data->blocks, data->blocksize); | |
344 | ||
345 | host->wait_int = 0; | |
346 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
347 | ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) & | |
348 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
349 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
350 | ~INFO1M_ACCESS_END & | |
351 | sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
352 | ||
353 | time = sh_sdhi_wait_interrupt_flag(host); | |
354 | if (time == 0 || host->sd_error != 0) | |
355 | return sh_sdhi_error_manage(host); | |
356 | ||
357 | host->wait_int = 0; | |
358 | blocksize = sh_sdhi_readw(host, SDHI_SIZE); | |
359 | for (i = 0; i < blocksize / 2; i++) | |
360 | sh_sdhi_writew(host, SDHI_BUF0, *p++); | |
361 | ||
362 | time = sh_sdhi_wait_interrupt_flag(host); | |
363 | if (time == 0 || host->sd_error != 0) | |
364 | return sh_sdhi_error_manage(host); | |
365 | ||
366 | host->wait_int = 0; | |
367 | return 0; | |
368 | } | |
369 | ||
370 | static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data) | |
371 | { | |
372 | long time; | |
373 | unsigned short i, sec, blocksize; | |
374 | const unsigned short *p = (const unsigned short *)data->src; | |
375 | ||
376 | debug("%s: blocks = %d, blocksize = %d\n", | |
377 | __func__, data->blocks, data->blocksize); | |
378 | ||
379 | host->wait_int = 0; | |
380 | for (sec = 0; sec < data->blocks; sec++) { | |
381 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
382 | ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) & | |
383 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
384 | ||
385 | time = sh_sdhi_wait_interrupt_flag(host); | |
386 | if (time == 0 || host->sd_error != 0) | |
387 | return sh_sdhi_error_manage(host); | |
388 | ||
389 | host->wait_int = 0; | |
390 | blocksize = sh_sdhi_readw(host, SDHI_SIZE); | |
391 | for (i = 0; i < blocksize / 2; i++) | |
392 | sh_sdhi_writew(host, SDHI_BUF0, *p++); | |
393 | } | |
394 | ||
395 | return 0; | |
396 | } | |
397 | ||
398 | static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd) | |
399 | { | |
400 | unsigned short i, j, cnt = 1; | |
401 | unsigned short resp[8]; | |
402 | unsigned long *p1, *p2; | |
403 | ||
404 | if (cmd->resp_type & MMC_RSP_136) { | |
405 | cnt = 4; | |
406 | resp[0] = sh_sdhi_readw(host, SDHI_RSP00); | |
407 | resp[1] = sh_sdhi_readw(host, SDHI_RSP01); | |
408 | resp[2] = sh_sdhi_readw(host, SDHI_RSP02); | |
409 | resp[3] = sh_sdhi_readw(host, SDHI_RSP03); | |
410 | resp[4] = sh_sdhi_readw(host, SDHI_RSP04); | |
411 | resp[5] = sh_sdhi_readw(host, SDHI_RSP05); | |
412 | resp[6] = sh_sdhi_readw(host, SDHI_RSP06); | |
413 | resp[7] = sh_sdhi_readw(host, SDHI_RSP07); | |
414 | ||
415 | /* SDHI REGISTER SPECIFICATION */ | |
416 | for (i = 7, j = 6; i > 0; i--) { | |
417 | resp[i] = (resp[i] << 8) & 0xff00; | |
418 | resp[i] |= (resp[j--] >> 8) & 0x00ff; | |
419 | } | |
420 | resp[0] = (resp[0] << 8) & 0xff00; | |
421 | ||
422 | /* SDHI REGISTER SPECIFICATION */ | |
423 | p1 = ((unsigned long *)resp) + 3; | |
424 | ||
425 | } else { | |
426 | resp[0] = sh_sdhi_readw(host, SDHI_RSP00); | |
427 | resp[1] = sh_sdhi_readw(host, SDHI_RSP01); | |
428 | ||
429 | p1 = ((unsigned long *)resp); | |
430 | } | |
431 | ||
432 | p2 = (unsigned long *)cmd->response; | |
433 | #if defined(__BIG_ENDIAN_BITFIELD) | |
434 | for (i = 0; i < cnt; i++) { | |
435 | *p2++ = ((*p1 >> 16) & 0x0000ffff) | | |
436 | ((*p1 << 16) & 0xffff0000); | |
437 | p1--; | |
438 | } | |
439 | #else | |
440 | for (i = 0; i < cnt; i++) | |
441 | *p2++ = *p1--; | |
442 | #endif /* __BIG_ENDIAN_BITFIELD */ | |
443 | } | |
444 | ||
445 | static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host, | |
446 | struct mmc_data *data, unsigned short opc) | |
447 | { | |
448 | switch (opc) { | |
449 | case SD_CMD_APP_SEND_OP_COND: | |
450 | case SD_CMD_APP_SEND_SCR: | |
451 | opc |= SDHI_APP; | |
452 | break; | |
453 | case SD_CMD_APP_SET_BUS_WIDTH: | |
454 | /* SD_APP_SET_BUS_WIDTH*/ | |
455 | if (!data) | |
456 | opc |= SDHI_APP; | |
457 | else /* SD_SWITCH */ | |
458 | opc = SDHI_SD_SWITCH; | |
459 | break; | |
460 | default: | |
461 | break; | |
462 | } | |
463 | return opc; | |
464 | } | |
465 | ||
466 | static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host, | |
467 | struct mmc_data *data, unsigned short opc) | |
468 | { | |
469 | unsigned short ret; | |
470 | ||
471 | switch (opc) { | |
472 | case MMC_CMD_READ_MULTIPLE_BLOCK: | |
473 | ret = sh_sdhi_multi_read(host, data); | |
474 | break; | |
475 | case MMC_CMD_WRITE_MULTIPLE_BLOCK: | |
476 | ret = sh_sdhi_multi_write(host, data); | |
477 | break; | |
478 | case MMC_CMD_WRITE_SINGLE_BLOCK: | |
479 | ret = sh_sdhi_single_write(host, data); | |
480 | break; | |
481 | case MMC_CMD_READ_SINGLE_BLOCK: | |
482 | case SDHI_SD_APP_SEND_SCR: | |
483 | case SDHI_SD_SWITCH: /* SD_SWITCH */ | |
484 | ret = sh_sdhi_single_read(host, data); | |
485 | break; | |
486 | default: | |
487 | printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc); | |
488 | ret = -EINVAL; | |
489 | break; | |
490 | } | |
491 | return ret; | |
492 | } | |
493 | ||
494 | static int sh_sdhi_start_cmd(struct sh_sdhi_host *host, | |
495 | struct mmc_data *data, struct mmc_cmd *cmd) | |
496 | { | |
497 | long time; | |
498 | unsigned short opc = cmd->cmdidx; | |
499 | int ret = 0; | |
500 | unsigned long timeout; | |
501 | ||
502 | debug("opc = %d, arg = %x, resp_type = %x\n", | |
503 | opc, cmd->cmdarg, cmd->resp_type); | |
504 | ||
505 | if (opc == MMC_CMD_STOP_TRANSMISSION) { | |
506 | /* SDHI sends the STOP command automatically by STOP reg */ | |
507 | sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END & | |
508 | sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
509 | ||
510 | time = sh_sdhi_wait_interrupt_flag(host); | |
511 | if (time == 0 || host->sd_error != 0) | |
512 | return sh_sdhi_error_manage(host); | |
513 | ||
514 | sh_sdhi_get_response(host, cmd); | |
515 | return 0; | |
516 | } | |
517 | ||
518 | if (data) { | |
519 | if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) || | |
520 | opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) { | |
521 | sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE); | |
522 | sh_sdhi_writew(host, SDHI_SECCNT, data->blocks); | |
523 | } | |
524 | sh_sdhi_writew(host, SDHI_SIZE, data->blocksize); | |
525 | } | |
526 | opc = sh_sdhi_set_cmd(host, data, opc); | |
527 | ||
528 | /* | |
a187559e | 529 | * U-Boot cannot use interrupt. |
72d42bad NI |
530 | * So this flag may not be clear by timing |
531 | */ | |
532 | sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END); | |
533 | ||
534 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
535 | INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
536 | sh_sdhi_writew(host, SDHI_ARG0, | |
537 | (unsigned short)(cmd->cmdarg & ARG0_MASK)); | |
538 | sh_sdhi_writew(host, SDHI_ARG1, | |
539 | (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK)); | |
540 | ||
541 | timeout = 100000; | |
542 | /* Waiting for SD Bus busy to be cleared */ | |
543 | while (timeout--) { | |
544 | if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000)) | |
545 | break; | |
546 | } | |
547 | ||
548 | sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK)); | |
549 | ||
550 | host->wait_int = 0; | |
551 | sh_sdhi_writew(host, SDHI_INFO1_MASK, | |
552 | ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK)); | |
553 | sh_sdhi_writew(host, SDHI_INFO2_MASK, | |
554 | ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR | | |
555 | INFO2M_END_ERROR | INFO2M_TIMEOUT | | |
556 | INFO2M_RESP_TIMEOUT | INFO2M_ILA) & | |
557 | sh_sdhi_readw(host, SDHI_INFO2_MASK)); | |
558 | ||
559 | time = sh_sdhi_wait_interrupt_flag(host); | |
560 | if (!time) | |
561 | return sh_sdhi_error_manage(host); | |
562 | ||
563 | if (host->sd_error) { | |
564 | switch (cmd->cmdidx) { | |
565 | case MMC_CMD_ALL_SEND_CID: | |
566 | case MMC_CMD_SELECT_CARD: | |
567 | case SD_CMD_SEND_IF_COND: | |
568 | case MMC_CMD_APP_CMD: | |
569 | ret = TIMEOUT; | |
570 | break; | |
571 | default: | |
572 | debug(DRIVER_NAME": Cmd(d'%d) err\n", opc); | |
573 | debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx); | |
574 | ret = sh_sdhi_error_manage(host); | |
575 | break; | |
576 | } | |
577 | host->sd_error = 0; | |
578 | host->wait_int = 0; | |
579 | return ret; | |
580 | } | |
581 | if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END) | |
582 | return -EINVAL; | |
583 | ||
584 | if (host->wait_int) { | |
585 | sh_sdhi_get_response(host, cmd); | |
586 | host->wait_int = 0; | |
587 | } | |
588 | if (data) | |
589 | ret = sh_sdhi_data_trans(host, data, opc); | |
590 | ||
591 | debug("ret = %d, resp = %08x, %08x, %08x, %08x\n", | |
592 | ret, cmd->response[0], cmd->response[1], | |
593 | cmd->response[2], cmd->response[3]); | |
594 | return ret; | |
595 | } | |
596 | ||
597 | static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, | |
598 | struct mmc_data *data) | |
599 | { | |
600 | struct sh_sdhi_host *host = mmc_priv(mmc); | |
601 | int ret; | |
602 | ||
603 | host->sd_error = 0; | |
604 | ||
605 | ret = sh_sdhi_start_cmd(host, data, cmd); | |
606 | ||
607 | return ret; | |
608 | } | |
609 | ||
610 | static void sh_sdhi_set_ios(struct mmc *mmc) | |
611 | { | |
612 | int ret; | |
613 | struct sh_sdhi_host *host = mmc_priv(mmc); | |
614 | ||
615 | ret = sh_sdhi_clock_control(host, mmc->clock); | |
616 | if (ret) | |
617 | return; | |
618 | ||
619 | if (mmc->bus_width == 4) | |
620 | sh_sdhi_writew(host, SDHI_OPTION, ~OPT_BUS_WIDTH_1 & | |
621 | sh_sdhi_readw(host, SDHI_OPTION)); | |
622 | else | |
623 | sh_sdhi_writew(host, SDHI_OPTION, OPT_BUS_WIDTH_1 | | |
624 | sh_sdhi_readw(host, SDHI_OPTION)); | |
625 | ||
626 | debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width); | |
627 | } | |
628 | ||
629 | static int sh_sdhi_initialize(struct mmc *mmc) | |
630 | { | |
631 | struct sh_sdhi_host *host = mmc_priv(mmc); | |
632 | int ret = sh_sdhi_sync_reset(host); | |
633 | ||
634 | sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT); | |
635 | ||
636 | #if defined(__BIG_ENDIAN_BITFIELD) | |
637 | sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP); | |
638 | #endif | |
639 | ||
640 | sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | | |
641 | INFO1M_ACCESS_END | INFO1M_CARD_RE | | |
642 | INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); | |
643 | ||
644 | return ret; | |
645 | } | |
646 | ||
647 | static const struct mmc_ops sh_sdhi_ops = { | |
648 | .send_cmd = sh_sdhi_send_cmd, | |
649 | .set_ios = sh_sdhi_set_ios, | |
650 | .init = sh_sdhi_initialize, | |
651 | }; | |
652 | ||
653 | static struct mmc_config sh_sdhi_cfg = { | |
654 | .name = DRIVER_NAME, | |
655 | .ops = &sh_sdhi_ops, | |
656 | .f_min = CLKDEV_INIT, | |
657 | .f_max = CLKDEV_HS_DATA, | |
658 | .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, | |
659 | .host_caps = MMC_MODE_4BIT | MMC_MODE_HS, | |
660 | .part_type = PART_TYPE_DOS, | |
661 | .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, | |
662 | }; | |
663 | ||
664 | int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks) | |
665 | { | |
666 | int ret = 0; | |
667 | struct mmc *mmc; | |
668 | struct sh_sdhi_host *host = NULL; | |
669 | ||
670 | if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL) | |
671 | return -ENODEV; | |
672 | ||
673 | host = malloc(sizeof(struct sh_sdhi_host)); | |
674 | if (!host) | |
675 | return -ENOMEM; | |
676 | ||
677 | mmc = mmc_create(&sh_sdhi_cfg, host); | |
678 | if (!mmc) { | |
679 | ret = -1; | |
680 | goto error; | |
681 | } | |
682 | ||
683 | host->ch = ch; | |
684 | host->addr = addr; | |
685 | host->quirks = quirks; | |
686 | ||
687 | if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) | |
688 | host->bus_shift = 1; | |
689 | ||
690 | return ret; | |
691 | error: | |
692 | if (host) | |
693 | free(host); | |
694 | return ret; | |
695 | } |