blob: d181b6390586683cb61503fc136e8543f66951f7 [file] [log] [blame]
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +09001/*
2 * drivers/mmc/sh_sdhi.c
3 *
4 * SD/MMC driver for Renesas rmobile ARM SoCs.
5 *
Kouei Abe5ff23f02017-05-13 15:51:16 +02006 * Copyright (C) 2011,2013-2017 Renesas Electronics Corporation
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +09007 * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
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>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090016#include <linux/errno.h>
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +090017#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
23struct 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};
Kouei Abe5ff23f02017-05-13 15:51:16 +020032
33static inline void sh_sdhi_writeq(struct sh_sdhi_host *host, int reg, u64 val)
34{
35 writeq(val, host->addr + (reg << host->bus_shift));
36}
37
38static inline u64 sh_sdhi_readq(struct sh_sdhi_host *host, int reg)
39{
40 return readq(host->addr + (reg << host->bus_shift));
41}
42
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +090043static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
44{
45 writew(val, host->addr + (reg << host->bus_shift));
46}
47
48static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
49{
50 return readw(host->addr + (reg << host->bus_shift));
51}
52
53static void *mmc_priv(struct mmc *mmc)
54{
55 return (void *)mmc->priv;
56}
57
58static void sh_sdhi_detect(struct sh_sdhi_host *host)
59{
60 sh_sdhi_writew(host, SDHI_OPTION,
61 OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
62
63 host->detect_waiting = 0;
64}
65
66static int sh_sdhi_intr(void *dev_id)
67{
68 struct sh_sdhi_host *host = dev_id;
69 int state1 = 0, state2 = 0;
70
71 state1 = sh_sdhi_readw(host, SDHI_INFO1);
72 state2 = sh_sdhi_readw(host, SDHI_INFO2);
73
74 debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
75
76 /* CARD Insert */
77 if (state1 & INFO1_CARD_IN) {
78 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
79 if (!host->detect_waiting) {
80 host->detect_waiting = 1;
81 sh_sdhi_detect(host);
82 }
83 sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
84 INFO1M_ACCESS_END | INFO1M_CARD_IN |
85 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
86 return -EAGAIN;
87 }
88 /* CARD Removal */
89 if (state1 & INFO1_CARD_RE) {
90 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
91 if (!host->detect_waiting) {
92 host->detect_waiting = 1;
93 sh_sdhi_detect(host);
94 }
95 sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
96 INFO1M_ACCESS_END | INFO1M_CARD_RE |
97 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
98 sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
99 sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
100 return -EAGAIN;
101 }
102
103 if (state2 & INFO2_ALL_ERR) {
104 sh_sdhi_writew(host, SDHI_INFO2,
105 (unsigned short)~(INFO2_ALL_ERR));
106 sh_sdhi_writew(host, SDHI_INFO2_MASK,
107 INFO2M_ALL_ERR |
108 sh_sdhi_readw(host, SDHI_INFO2_MASK));
109 host->sd_error = 1;
110 host->wait_int = 1;
111 return 0;
112 }
113 /* Respons End */
114 if (state1 & INFO1_RESP_END) {
115 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
116 sh_sdhi_writew(host, SDHI_INFO1_MASK,
117 INFO1M_RESP_END |
118 sh_sdhi_readw(host, SDHI_INFO1_MASK));
119 host->wait_int = 1;
120 return 0;
121 }
122 /* SD_BUF Read Enable */
123 if (state2 & INFO2_BRE_ENABLE) {
124 sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
125 sh_sdhi_writew(host, SDHI_INFO2_MASK,
126 INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
127 sh_sdhi_readw(host, SDHI_INFO2_MASK));
128 host->wait_int = 1;
129 return 0;
130 }
131 /* SD_BUF Write Enable */
132 if (state2 & INFO2_BWE_ENABLE) {
133 sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
134 sh_sdhi_writew(host, SDHI_INFO2_MASK,
135 INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
136 sh_sdhi_readw(host, SDHI_INFO2_MASK));
137 host->wait_int = 1;
138 return 0;
139 }
140 /* Access End */
141 if (state1 & INFO1_ACCESS_END) {
142 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
143 sh_sdhi_writew(host, SDHI_INFO1_MASK,
144 INFO1_ACCESS_END |
145 sh_sdhi_readw(host, SDHI_INFO1_MASK));
146 host->wait_int = 1;
147 return 0;
148 }
149 return -EAGAIN;
150}
151
152static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
153{
154 int timeout = 10000000;
155
156 while (1) {
157 timeout--;
158 if (timeout < 0) {
159 debug(DRIVER_NAME": %s timeout\n", __func__);
160 return 0;
161 }
162
163 if (!sh_sdhi_intr(host))
164 break;
165
166 udelay(1); /* 1 usec */
167 }
168
169 return 1; /* Return value: NOT 0 = complete waiting */
170}
171
172static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
173{
174 u32 clkdiv, i, timeout;
175
176 if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
177 printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
178 return -EBUSY;
179 }
180
181 sh_sdhi_writew(host, SDHI_CLK_CTRL,
182 ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
183
184 if (clk == 0)
185 return -EIO;
186
187 clkdiv = 0x80;
188 i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
189 for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
190 i <<= 1;
191
192 sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
193
194 timeout = 100000;
195 /* Waiting for SD Bus busy to be cleared */
196 while (timeout--) {
197 if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
198 break;
199 }
200
201 if (timeout)
202 sh_sdhi_writew(host, SDHI_CLK_CTRL,
203 CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
204 else
205 return -EBUSY;
206
207 return 0;
208}
209
210static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
211{
212 u32 timeout;
213 sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
214 sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
215 sh_sdhi_writew(host, SDHI_CLK_CTRL,
216 CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
217
218 timeout = 100000;
219 while (timeout--) {
220 if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
221 break;
222 udelay(100);
223 }
224
225 if (!timeout)
226 return -EBUSY;
227
228 if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
229 sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
230
231 return 0;
232}
233
234static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
235{
236 unsigned short e_state1, e_state2;
237 int ret;
238
239 host->sd_error = 0;
240 host->wait_int = 0;
241
242 e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
243 e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
244 if (e_state2 & ERR_STS2_SYS_ERROR) {
245 if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
Jaehoon Chung7825d202016-07-19 16:33:36 +0900246 ret = -ETIMEDOUT;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900247 else
248 ret = -EILSEQ;
249 debug("%s: ERR_STS2 = %04x\n",
250 DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
251 sh_sdhi_sync_reset(host);
252
253 sh_sdhi_writew(host, SDHI_INFO1_MASK,
254 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
255 return ret;
256 }
257 if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
258 ret = -EILSEQ;
259 else
Jaehoon Chung7825d202016-07-19 16:33:36 +0900260 ret = -ETIMEDOUT;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900261
262 debug("%s: ERR_STS1 = %04x\n",
263 DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
264 sh_sdhi_sync_reset(host);
265 sh_sdhi_writew(host, SDHI_INFO1_MASK,
266 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
267 return ret;
268}
269
270static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
271{
272 long time;
273 unsigned short blocksize, i;
274 unsigned short *p = (unsigned short *)data->dest;
Kouei Abe5ff23f02017-05-13 15:51:16 +0200275 u64 *q = (u64 *)data->dest;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900276
277 if ((unsigned long)p & 0x00000001) {
278 debug(DRIVER_NAME": %s: The data pointer is unaligned.",
279 __func__);
280 return -EIO;
281 }
282
283 host->wait_int = 0;
284 sh_sdhi_writew(host, SDHI_INFO2_MASK,
285 ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
286 sh_sdhi_readw(host, SDHI_INFO2_MASK));
287 sh_sdhi_writew(host, SDHI_INFO1_MASK,
288 ~INFO1M_ACCESS_END &
289 sh_sdhi_readw(host, SDHI_INFO1_MASK));
290 time = sh_sdhi_wait_interrupt_flag(host);
291 if (time == 0 || host->sd_error != 0)
292 return sh_sdhi_error_manage(host);
293
294 host->wait_int = 0;
295 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5ff23f02017-05-13 15:51:16 +0200296 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
297 for (i = 0; i < blocksize / 8; i++)
298 *q++ = sh_sdhi_readq(host, SDHI_BUF0);
299 else
300 for (i = 0; i < blocksize / 2; i++)
301 *p++ = sh_sdhi_readw(host, SDHI_BUF0);
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900302
303 time = sh_sdhi_wait_interrupt_flag(host);
304 if (time == 0 || host->sd_error != 0)
305 return sh_sdhi_error_manage(host);
306
307 host->wait_int = 0;
308 return 0;
309}
310
311static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
312{
313 long time;
314 unsigned short blocksize, i, sec;
315 unsigned short *p = (unsigned short *)data->dest;
Kouei Abe5ff23f02017-05-13 15:51:16 +0200316 u64 *q = (u64 *)data->dest;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900317
318 if ((unsigned long)p & 0x00000001) {
319 debug(DRIVER_NAME": %s: The data pointer is unaligned.",
320 __func__);
321 return -EIO;
322 }
323
324 debug("%s: blocks = %d, blocksize = %d\n",
325 __func__, data->blocks, data->blocksize);
326
327 host->wait_int = 0;
328 for (sec = 0; sec < data->blocks; sec++) {
329 sh_sdhi_writew(host, SDHI_INFO2_MASK,
330 ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
331 sh_sdhi_readw(host, SDHI_INFO2_MASK));
332
333 time = sh_sdhi_wait_interrupt_flag(host);
334 if (time == 0 || host->sd_error != 0)
335 return sh_sdhi_error_manage(host);
336
337 host->wait_int = 0;
338 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5ff23f02017-05-13 15:51:16 +0200339 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
340 for (i = 0; i < blocksize / 8; i++)
341 *q++ = sh_sdhi_readq(host, SDHI_BUF0);
342 else
343 for (i = 0; i < blocksize / 2; i++)
344 *p++ = sh_sdhi_readw(host, SDHI_BUF0);
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900345 }
346
347 return 0;
348}
349
350static int sh_sdhi_single_write(struct sh_sdhi_host *host,
351 struct mmc_data *data)
352{
353 long time;
354 unsigned short blocksize, i;
355 const unsigned short *p = (const unsigned short *)data->src;
Kouei Abe5ff23f02017-05-13 15:51:16 +0200356 const u64 *q = (const u64 *)data->src;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900357
358 if ((unsigned long)p & 0x00000001) {
359 debug(DRIVER_NAME": %s: The data pointer is unaligned.",
360 __func__);
361 return -EIO;
362 }
363
364 debug("%s: blocks = %d, blocksize = %d\n",
365 __func__, data->blocks, data->blocksize);
366
367 host->wait_int = 0;
368 sh_sdhi_writew(host, SDHI_INFO2_MASK,
369 ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
370 sh_sdhi_readw(host, SDHI_INFO2_MASK));
371 sh_sdhi_writew(host, SDHI_INFO1_MASK,
372 ~INFO1M_ACCESS_END &
373 sh_sdhi_readw(host, SDHI_INFO1_MASK));
374
375 time = sh_sdhi_wait_interrupt_flag(host);
376 if (time == 0 || host->sd_error != 0)
377 return sh_sdhi_error_manage(host);
378
379 host->wait_int = 0;
380 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5ff23f02017-05-13 15:51:16 +0200381 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
382 for (i = 0; i < blocksize / 8; i++)
383 sh_sdhi_writeq(host, SDHI_BUF0, *q++);
384 else
385 for (i = 0; i < blocksize / 2; i++)
386 sh_sdhi_writew(host, SDHI_BUF0, *p++);
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900387
388 time = sh_sdhi_wait_interrupt_flag(host);
389 if (time == 0 || host->sd_error != 0)
390 return sh_sdhi_error_manage(host);
391
392 host->wait_int = 0;
393 return 0;
394}
395
396static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
397{
398 long time;
399 unsigned short i, sec, blocksize;
400 const unsigned short *p = (const unsigned short *)data->src;
Kouei Abe5ff23f02017-05-13 15:51:16 +0200401 const u64 *q = (const u64 *)data->src;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900402
403 debug("%s: blocks = %d, blocksize = %d\n",
404 __func__, data->blocks, data->blocksize);
405
406 host->wait_int = 0;
407 for (sec = 0; sec < data->blocks; sec++) {
408 sh_sdhi_writew(host, SDHI_INFO2_MASK,
409 ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
410 sh_sdhi_readw(host, SDHI_INFO2_MASK));
411
412 time = sh_sdhi_wait_interrupt_flag(host);
413 if (time == 0 || host->sd_error != 0)
414 return sh_sdhi_error_manage(host);
415
416 host->wait_int = 0;
417 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5ff23f02017-05-13 15:51:16 +0200418 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
419 for (i = 0; i < blocksize / 8; i++)
420 sh_sdhi_writeq(host, SDHI_BUF0, *q++);
421 else
422 for (i = 0; i < blocksize / 2; i++)
423 sh_sdhi_writew(host, SDHI_BUF0, *p++);
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900424 }
425
426 return 0;
427}
428
429static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
430{
431 unsigned short i, j, cnt = 1;
432 unsigned short resp[8];
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900433
434 if (cmd->resp_type & MMC_RSP_136) {
435 cnt = 4;
436 resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
437 resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
438 resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
439 resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
440 resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
441 resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
442 resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
443 resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
444
445 /* SDHI REGISTER SPECIFICATION */
446 for (i = 7, j = 6; i > 0; i--) {
447 resp[i] = (resp[i] << 8) & 0xff00;
448 resp[i] |= (resp[j--] >> 8) & 0x00ff;
449 }
450 resp[0] = (resp[0] << 8) & 0xff00;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900451 } else {
452 resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
453 resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900454 }
455
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900456#if defined(__BIG_ENDIAN_BITFIELD)
masakazu.mochizuki.wd@hitachi.com9d0e9372016-04-12 17:11:41 +0900457 if (cnt == 4) {
458 cmd->response[0] = (resp[6] << 16) | resp[7];
459 cmd->response[1] = (resp[4] << 16) | resp[5];
460 cmd->response[2] = (resp[2] << 16) | resp[3];
461 cmd->response[3] = (resp[0] << 16) | resp[1];
462 } else {
463 cmd->response[0] = (resp[0] << 16) | resp[1];
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900464 }
465#else
masakazu.mochizuki.wd@hitachi.com9d0e9372016-04-12 17:11:41 +0900466 if (cnt == 4) {
467 cmd->response[0] = (resp[7] << 16) | resp[6];
468 cmd->response[1] = (resp[5] << 16) | resp[4];
469 cmd->response[2] = (resp[3] << 16) | resp[2];
470 cmd->response[3] = (resp[1] << 16) | resp[0];
471 } else {
472 cmd->response[0] = (resp[1] << 16) | resp[0];
473 }
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900474#endif /* __BIG_ENDIAN_BITFIELD */
475}
476
477static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
478 struct mmc_data *data, unsigned short opc)
479{
480 switch (opc) {
481 case SD_CMD_APP_SEND_OP_COND:
482 case SD_CMD_APP_SEND_SCR:
483 opc |= SDHI_APP;
484 break;
485 case SD_CMD_APP_SET_BUS_WIDTH:
486 /* SD_APP_SET_BUS_WIDTH*/
487 if (!data)
488 opc |= SDHI_APP;
489 else /* SD_SWITCH */
490 opc = SDHI_SD_SWITCH;
491 break;
Kouei Abe44b13ef2017-05-13 15:51:17 +0200492 case MMC_CMD_SEND_OP_COND:
493 opc = SDHI_MMC_SEND_OP_COND;
494 break;
495 case MMC_CMD_SEND_EXT_CSD:
496 if (data)
497 opc = SDHI_MMC_SEND_EXT_CSD;
498 break;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900499 default:
500 break;
501 }
502 return opc;
503}
504
505static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
506 struct mmc_data *data, unsigned short opc)
507{
508 unsigned short ret;
509
510 switch (opc) {
511 case MMC_CMD_READ_MULTIPLE_BLOCK:
512 ret = sh_sdhi_multi_read(host, data);
513 break;
514 case MMC_CMD_WRITE_MULTIPLE_BLOCK:
515 ret = sh_sdhi_multi_write(host, data);
516 break;
517 case MMC_CMD_WRITE_SINGLE_BLOCK:
518 ret = sh_sdhi_single_write(host, data);
519 break;
520 case MMC_CMD_READ_SINGLE_BLOCK:
521 case SDHI_SD_APP_SEND_SCR:
522 case SDHI_SD_SWITCH: /* SD_SWITCH */
Kouei Abe44b13ef2017-05-13 15:51:17 +0200523 case SDHI_MMC_SEND_EXT_CSD:
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900524 ret = sh_sdhi_single_read(host, data);
525 break;
526 default:
527 printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
528 ret = -EINVAL;
529 break;
530 }
531 return ret;
532}
533
534static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
535 struct mmc_data *data, struct mmc_cmd *cmd)
536{
537 long time;
538 unsigned short opc = cmd->cmdidx;
539 int ret = 0;
540 unsigned long timeout;
541
542 debug("opc = %d, arg = %x, resp_type = %x\n",
543 opc, cmd->cmdarg, cmd->resp_type);
544
545 if (opc == MMC_CMD_STOP_TRANSMISSION) {
546 /* SDHI sends the STOP command automatically by STOP reg */
547 sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
548 sh_sdhi_readw(host, SDHI_INFO1_MASK));
549
550 time = sh_sdhi_wait_interrupt_flag(host);
551 if (time == 0 || host->sd_error != 0)
552 return sh_sdhi_error_manage(host);
553
554 sh_sdhi_get_response(host, cmd);
555 return 0;
556 }
557
558 if (data) {
559 if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
560 opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
561 sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
562 sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
563 }
564 sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
565 }
566 opc = sh_sdhi_set_cmd(host, data, opc);
567
568 /*
Bin Meng75574052016-02-05 19:30:11 -0800569 * U-Boot cannot use interrupt.
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900570 * So this flag may not be clear by timing
571 */
572 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
573
574 sh_sdhi_writew(host, SDHI_INFO1_MASK,
575 INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
576 sh_sdhi_writew(host, SDHI_ARG0,
577 (unsigned short)(cmd->cmdarg & ARG0_MASK));
578 sh_sdhi_writew(host, SDHI_ARG1,
579 (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
580
581 timeout = 100000;
582 /* Waiting for SD Bus busy to be cleared */
583 while (timeout--) {
584 if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
585 break;
586 }
587
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900588 host->wait_int = 0;
589 sh_sdhi_writew(host, SDHI_INFO1_MASK,
590 ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
591 sh_sdhi_writew(host, SDHI_INFO2_MASK,
592 ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
593 INFO2M_END_ERROR | INFO2M_TIMEOUT |
594 INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
595 sh_sdhi_readw(host, SDHI_INFO2_MASK));
596
Kouei Abe4b8bb452017-05-13 15:51:15 +0200597 sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
598
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900599 time = sh_sdhi_wait_interrupt_flag(host);
600 if (!time)
601 return sh_sdhi_error_manage(host);
602
603 if (host->sd_error) {
604 switch (cmd->cmdidx) {
605 case MMC_CMD_ALL_SEND_CID:
606 case MMC_CMD_SELECT_CARD:
607 case SD_CMD_SEND_IF_COND:
608 case MMC_CMD_APP_CMD:
Jaehoon Chung7825d202016-07-19 16:33:36 +0900609 ret = -ETIMEDOUT;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900610 break;
611 default:
612 debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
613 debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
614 ret = sh_sdhi_error_manage(host);
615 break;
616 }
617 host->sd_error = 0;
618 host->wait_int = 0;
619 return ret;
620 }
621 if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END)
622 return -EINVAL;
623
624 if (host->wait_int) {
625 sh_sdhi_get_response(host, cmd);
626 host->wait_int = 0;
627 }
628 if (data)
629 ret = sh_sdhi_data_trans(host, data, opc);
630
631 debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
632 ret, cmd->response[0], cmd->response[1],
633 cmd->response[2], cmd->response[3]);
634 return ret;
635}
636
637static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
638 struct mmc_data *data)
639{
640 struct sh_sdhi_host *host = mmc_priv(mmc);
641 int ret;
642
643 host->sd_error = 0;
644
645 ret = sh_sdhi_start_cmd(host, data, cmd);
646
647 return ret;
648}
649
Jaehoon Chungb6cd1d32016-12-30 15:30:16 +0900650static int sh_sdhi_set_ios(struct mmc *mmc)
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900651{
652 int ret;
653 struct sh_sdhi_host *host = mmc_priv(mmc);
654
655 ret = sh_sdhi_clock_control(host, mmc->clock);
656 if (ret)
Jaehoon Chungb6cd1d32016-12-30 15:30:16 +0900657 return -EINVAL;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900658
Kouei Abe44b13ef2017-05-13 15:51:17 +0200659 if (mmc->bus_width == 8)
660 sh_sdhi_writew(host, SDHI_OPTION,
661 OPT_BUS_WIDTH_8 | (~OPT_BUS_WIDTH_M &
662 sh_sdhi_readw(host, SDHI_OPTION)));
663 else if (mmc->bus_width == 4)
664 sh_sdhi_writew(host, SDHI_OPTION,
665 OPT_BUS_WIDTH_4 | (~OPT_BUS_WIDTH_M &
666 sh_sdhi_readw(host, SDHI_OPTION)));
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900667 else
Kouei Abe44b13ef2017-05-13 15:51:17 +0200668 sh_sdhi_writew(host, SDHI_OPTION,
669 OPT_BUS_WIDTH_1 | (~OPT_BUS_WIDTH_M &
670 sh_sdhi_readw(host, SDHI_OPTION)));
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900671
672 debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
Jaehoon Chungb6cd1d32016-12-30 15:30:16 +0900673
674 return 0;
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900675}
676
677static int sh_sdhi_initialize(struct mmc *mmc)
678{
679 struct sh_sdhi_host *host = mmc_priv(mmc);
680 int ret = sh_sdhi_sync_reset(host);
681
682 sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
683
684#if defined(__BIG_ENDIAN_BITFIELD)
685 sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
686#endif
687
688 sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
689 INFO1M_ACCESS_END | INFO1M_CARD_RE |
690 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
691
692 return ret;
693}
694
695static const struct mmc_ops sh_sdhi_ops = {
696 .send_cmd = sh_sdhi_send_cmd,
697 .set_ios = sh_sdhi_set_ios,
698 .init = sh_sdhi_initialize,
699};
700
Kouei Abe22dbc5f2017-05-13 15:51:18 +0200701#ifdef CONFIG_RCAR_GEN3
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900702static struct mmc_config sh_sdhi_cfg = {
703 .name = DRIVER_NAME,
704 .ops = &sh_sdhi_ops,
705 .f_min = CLKDEV_INIT,
706 .f_max = CLKDEV_HS_DATA,
Kouei Abe22dbc5f2017-05-13 15:51:18 +0200707 .voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
708 .host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS |
709 MMC_MODE_HS_52MHz,
710 .part_type = PART_TYPE_DOS,
711 .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
712};
713#else
714static struct mmc_config sh_sdhi_cfg = {
715 .name = DRIVER_NAME,
716 .ops = &sh_sdhi_ops,
717 .f_min = CLKDEV_INIT,
718 .f_max = CLKDEV_HS_DATA,
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900719 .voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
720 .host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
721 .part_type = PART_TYPE_DOS,
722 .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
723};
Kouei Abe22dbc5f2017-05-13 15:51:18 +0200724#endif
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900725
726int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
727{
728 int ret = 0;
729 struct mmc *mmc;
730 struct sh_sdhi_host *host = NULL;
731
732 if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
733 return -ENODEV;
734
735 host = malloc(sizeof(struct sh_sdhi_host));
736 if (!host)
737 return -ENOMEM;
738
739 mmc = mmc_create(&sh_sdhi_cfg, host);
740 if (!mmc) {
741 ret = -1;
742 goto error;
743 }
744
745 host->ch = ch;
746 host->addr = addr;
747 host->quirks = quirks;
748
Kouei Abe5ff23f02017-05-13 15:51:16 +0200749 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
750 host->bus_shift = 2;
751 else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
Nobuhiro Iwamatsu3ec5f862014-12-17 08:03:00 +0900752 host->bus_shift = 1;
753
754 return ret;
755error:
756 if (host)
757 free(host);
758 return ret;
759}