blob: fd710399b6830807c9f2fd1e469d8b13a837020b [file] [log] [blame]
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +09001/*
2 * drivers/mmc/sh_sdhi.c
3 *
4 * SD/MMC driver for Renesas rmobile ARM SoCs.
5 *
Kouei Abe5eada1d2017-05-13 15:51:16 +02006 * Copyright (C) 2011,2013-2017 Renesas Electronics Corporation
Nobuhiro Iwamatsu72d42ba2014-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>
Marek Vasutd1c18ca2017-07-21 23:22:54 +020016#include <dm.h>
Masahiro Yamada1221ce42016-09-21 11:28:55 +090017#include <linux/errno.h>
Marek Vasutd1c18ca2017-07-21 23:22:54 +020018#include <linux/compat.h>
19#include <linux/io.h>
20#include <linux/sizes.h>
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +090021#include <asm/arch/rmobile.h>
22#include <asm/arch/sh_sdhi.h>
23
24#define DRIVER_NAME "sh-sdhi"
25
26struct sh_sdhi_host {
Marek Vasutd1c18ca2017-07-21 23:22:54 +020027 void __iomem *addr;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +090028 int ch;
29 int bus_shift;
30 unsigned long quirks;
31 unsigned char wait_int;
32 unsigned char sd_error;
33 unsigned char detect_waiting;
34};
Kouei Abe5eada1d2017-05-13 15:51:16 +020035
36static inline void sh_sdhi_writeq(struct sh_sdhi_host *host, int reg, u64 val)
37{
38 writeq(val, host->addr + (reg << host->bus_shift));
39}
40
41static inline u64 sh_sdhi_readq(struct sh_sdhi_host *host, int reg)
42{
43 return readq(host->addr + (reg << host->bus_shift));
44}
45
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +090046static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
47{
48 writew(val, host->addr + (reg << host->bus_shift));
49}
50
51static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
52{
53 return readw(host->addr + (reg << host->bus_shift));
54}
55
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +090056static void sh_sdhi_detect(struct sh_sdhi_host *host)
57{
58 sh_sdhi_writew(host, SDHI_OPTION,
59 OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
60
61 host->detect_waiting = 0;
62}
63
64static int sh_sdhi_intr(void *dev_id)
65{
66 struct sh_sdhi_host *host = dev_id;
67 int state1 = 0, state2 = 0;
68
69 state1 = sh_sdhi_readw(host, SDHI_INFO1);
70 state2 = sh_sdhi_readw(host, SDHI_INFO2);
71
72 debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
73
74 /* CARD Insert */
75 if (state1 & INFO1_CARD_IN) {
76 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
77 if (!host->detect_waiting) {
78 host->detect_waiting = 1;
79 sh_sdhi_detect(host);
80 }
81 sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
82 INFO1M_ACCESS_END | INFO1M_CARD_IN |
83 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
84 return -EAGAIN;
85 }
86 /* CARD Removal */
87 if (state1 & INFO1_CARD_RE) {
88 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
89 if (!host->detect_waiting) {
90 host->detect_waiting = 1;
91 sh_sdhi_detect(host);
92 }
93 sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
94 INFO1M_ACCESS_END | INFO1M_CARD_RE |
95 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
96 sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
97 sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
98 return -EAGAIN;
99 }
100
101 if (state2 & INFO2_ALL_ERR) {
102 sh_sdhi_writew(host, SDHI_INFO2,
103 (unsigned short)~(INFO2_ALL_ERR));
104 sh_sdhi_writew(host, SDHI_INFO2_MASK,
105 INFO2M_ALL_ERR |
106 sh_sdhi_readw(host, SDHI_INFO2_MASK));
107 host->sd_error = 1;
108 host->wait_int = 1;
109 return 0;
110 }
111 /* Respons End */
112 if (state1 & INFO1_RESP_END) {
113 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
114 sh_sdhi_writew(host, SDHI_INFO1_MASK,
115 INFO1M_RESP_END |
116 sh_sdhi_readw(host, SDHI_INFO1_MASK));
117 host->wait_int = 1;
118 return 0;
119 }
120 /* SD_BUF Read Enable */
121 if (state2 & INFO2_BRE_ENABLE) {
122 sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
123 sh_sdhi_writew(host, SDHI_INFO2_MASK,
124 INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
125 sh_sdhi_readw(host, SDHI_INFO2_MASK));
126 host->wait_int = 1;
127 return 0;
128 }
129 /* SD_BUF Write Enable */
130 if (state2 & INFO2_BWE_ENABLE) {
131 sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
132 sh_sdhi_writew(host, SDHI_INFO2_MASK,
133 INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
134 sh_sdhi_readw(host, SDHI_INFO2_MASK));
135 host->wait_int = 1;
136 return 0;
137 }
138 /* Access End */
139 if (state1 & INFO1_ACCESS_END) {
140 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
141 sh_sdhi_writew(host, SDHI_INFO1_MASK,
142 INFO1_ACCESS_END |
143 sh_sdhi_readw(host, SDHI_INFO1_MASK));
144 host->wait_int = 1;
145 return 0;
146 }
147 return -EAGAIN;
148}
149
150static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
151{
152 int timeout = 10000000;
153
154 while (1) {
155 timeout--;
156 if (timeout < 0) {
157 debug(DRIVER_NAME": %s timeout\n", __func__);
158 return 0;
159 }
160
161 if (!sh_sdhi_intr(host))
162 break;
163
164 udelay(1); /* 1 usec */
165 }
166
167 return 1; /* Return value: NOT 0 = complete waiting */
168}
169
170static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
171{
172 u32 clkdiv, i, timeout;
173
174 if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
175 printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
176 return -EBUSY;
177 }
178
179 sh_sdhi_writew(host, SDHI_CLK_CTRL,
180 ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
181
182 if (clk == 0)
183 return -EIO;
184
185 clkdiv = 0x80;
186 i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
187 for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
188 i <<= 1;
189
190 sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
191
192 timeout = 100000;
193 /* Waiting for SD Bus busy to be cleared */
194 while (timeout--) {
195 if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
196 break;
197 }
198
199 if (timeout)
200 sh_sdhi_writew(host, SDHI_CLK_CTRL,
201 CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
202 else
203 return -EBUSY;
204
205 return 0;
206}
207
208static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
209{
210 u32 timeout;
211 sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
212 sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
213 sh_sdhi_writew(host, SDHI_CLK_CTRL,
214 CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
215
216 timeout = 100000;
217 while (timeout--) {
218 if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
219 break;
220 udelay(100);
221 }
222
223 if (!timeout)
224 return -EBUSY;
225
226 if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
227 sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
228
229 return 0;
230}
231
232static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
233{
234 unsigned short e_state1, e_state2;
235 int ret;
236
237 host->sd_error = 0;
238 host->wait_int = 0;
239
240 e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
241 e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
242 if (e_state2 & ERR_STS2_SYS_ERROR) {
243 if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
Jaehoon Chung915ffa52016-07-19 16:33:36 +0900244 ret = -ETIMEDOUT;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900245 else
246 ret = -EILSEQ;
247 debug("%s: ERR_STS2 = %04x\n",
248 DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
249 sh_sdhi_sync_reset(host);
250
251 sh_sdhi_writew(host, SDHI_INFO1_MASK,
252 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
253 return ret;
254 }
255 if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
256 ret = -EILSEQ;
257 else
Jaehoon Chung915ffa52016-07-19 16:33:36 +0900258 ret = -ETIMEDOUT;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900259
260 debug("%s: ERR_STS1 = %04x\n",
261 DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
262 sh_sdhi_sync_reset(host);
263 sh_sdhi_writew(host, SDHI_INFO1_MASK,
264 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
265 return ret;
266}
267
268static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
269{
270 long time;
271 unsigned short blocksize, i;
272 unsigned short *p = (unsigned short *)data->dest;
Kouei Abe5eada1d2017-05-13 15:51:16 +0200273 u64 *q = (u64 *)data->dest;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900274
275 if ((unsigned long)p & 0x00000001) {
276 debug(DRIVER_NAME": %s: The data pointer is unaligned.",
277 __func__);
278 return -EIO;
279 }
280
281 host->wait_int = 0;
282 sh_sdhi_writew(host, SDHI_INFO2_MASK,
283 ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
284 sh_sdhi_readw(host, SDHI_INFO2_MASK));
285 sh_sdhi_writew(host, SDHI_INFO1_MASK,
286 ~INFO1M_ACCESS_END &
287 sh_sdhi_readw(host, SDHI_INFO1_MASK));
288 time = sh_sdhi_wait_interrupt_flag(host);
289 if (time == 0 || host->sd_error != 0)
290 return sh_sdhi_error_manage(host);
291
292 host->wait_int = 0;
293 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5eada1d2017-05-13 15:51:16 +0200294 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
295 for (i = 0; i < blocksize / 8; i++)
296 *q++ = sh_sdhi_readq(host, SDHI_BUF0);
297 else
298 for (i = 0; i < blocksize / 2; i++)
299 *p++ = sh_sdhi_readw(host, SDHI_BUF0);
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900300
301 time = sh_sdhi_wait_interrupt_flag(host);
302 if (time == 0 || host->sd_error != 0)
303 return sh_sdhi_error_manage(host);
304
305 host->wait_int = 0;
306 return 0;
307}
308
309static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
310{
311 long time;
312 unsigned short blocksize, i, sec;
313 unsigned short *p = (unsigned short *)data->dest;
Kouei Abe5eada1d2017-05-13 15:51:16 +0200314 u64 *q = (u64 *)data->dest;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900315
316 if ((unsigned long)p & 0x00000001) {
317 debug(DRIVER_NAME": %s: The data pointer is unaligned.",
318 __func__);
319 return -EIO;
320 }
321
322 debug("%s: blocks = %d, blocksize = %d\n",
323 __func__, data->blocks, data->blocksize);
324
325 host->wait_int = 0;
326 for (sec = 0; sec < data->blocks; sec++) {
327 sh_sdhi_writew(host, SDHI_INFO2_MASK,
328 ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
329 sh_sdhi_readw(host, SDHI_INFO2_MASK));
330
331 time = sh_sdhi_wait_interrupt_flag(host);
332 if (time == 0 || host->sd_error != 0)
333 return sh_sdhi_error_manage(host);
334
335 host->wait_int = 0;
336 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5eada1d2017-05-13 15:51:16 +0200337 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
338 for (i = 0; i < blocksize / 8; i++)
339 *q++ = sh_sdhi_readq(host, SDHI_BUF0);
340 else
341 for (i = 0; i < blocksize / 2; i++)
342 *p++ = sh_sdhi_readw(host, SDHI_BUF0);
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900343 }
344
345 return 0;
346}
347
348static int sh_sdhi_single_write(struct sh_sdhi_host *host,
349 struct mmc_data *data)
350{
351 long time;
352 unsigned short blocksize, i;
353 const unsigned short *p = (const unsigned short *)data->src;
Kouei Abe5eada1d2017-05-13 15:51:16 +0200354 const u64 *q = (const u64 *)data->src;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900355
356 if ((unsigned long)p & 0x00000001) {
357 debug(DRIVER_NAME": %s: The data pointer is unaligned.",
358 __func__);
359 return -EIO;
360 }
361
362 debug("%s: blocks = %d, blocksize = %d\n",
363 __func__, data->blocks, data->blocksize);
364
365 host->wait_int = 0;
366 sh_sdhi_writew(host, SDHI_INFO2_MASK,
367 ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
368 sh_sdhi_readw(host, SDHI_INFO2_MASK));
369 sh_sdhi_writew(host, SDHI_INFO1_MASK,
370 ~INFO1M_ACCESS_END &
371 sh_sdhi_readw(host, SDHI_INFO1_MASK));
372
373 time = sh_sdhi_wait_interrupt_flag(host);
374 if (time == 0 || host->sd_error != 0)
375 return sh_sdhi_error_manage(host);
376
377 host->wait_int = 0;
378 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5eada1d2017-05-13 15:51:16 +0200379 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
380 for (i = 0; i < blocksize / 8; i++)
381 sh_sdhi_writeq(host, SDHI_BUF0, *q++);
382 else
383 for (i = 0; i < blocksize / 2; i++)
384 sh_sdhi_writew(host, SDHI_BUF0, *p++);
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900385
386 time = sh_sdhi_wait_interrupt_flag(host);
387 if (time == 0 || host->sd_error != 0)
388 return sh_sdhi_error_manage(host);
389
390 host->wait_int = 0;
391 return 0;
392}
393
394static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
395{
396 long time;
397 unsigned short i, sec, blocksize;
398 const unsigned short *p = (const unsigned short *)data->src;
Kouei Abe5eada1d2017-05-13 15:51:16 +0200399 const u64 *q = (const u64 *)data->src;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900400
401 debug("%s: blocks = %d, blocksize = %d\n",
402 __func__, data->blocks, data->blocksize);
403
404 host->wait_int = 0;
405 for (sec = 0; sec < data->blocks; sec++) {
406 sh_sdhi_writew(host, SDHI_INFO2_MASK,
407 ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
408 sh_sdhi_readw(host, SDHI_INFO2_MASK));
409
410 time = sh_sdhi_wait_interrupt_flag(host);
411 if (time == 0 || host->sd_error != 0)
412 return sh_sdhi_error_manage(host);
413
414 host->wait_int = 0;
415 blocksize = sh_sdhi_readw(host, SDHI_SIZE);
Kouei Abe5eada1d2017-05-13 15:51:16 +0200416 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
417 for (i = 0; i < blocksize / 8; i++)
418 sh_sdhi_writeq(host, SDHI_BUF0, *q++);
419 else
420 for (i = 0; i < blocksize / 2; i++)
421 sh_sdhi_writew(host, SDHI_BUF0, *p++);
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900422 }
423
424 return 0;
425}
426
427static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
428{
429 unsigned short i, j, cnt = 1;
430 unsigned short resp[8];
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900431
432 if (cmd->resp_type & MMC_RSP_136) {
433 cnt = 4;
434 resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
435 resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
436 resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
437 resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
438 resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
439 resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
440 resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
441 resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
442
443 /* SDHI REGISTER SPECIFICATION */
444 for (i = 7, j = 6; i > 0; i--) {
445 resp[i] = (resp[i] << 8) & 0xff00;
446 resp[i] |= (resp[j--] >> 8) & 0x00ff;
447 }
448 resp[0] = (resp[0] << 8) & 0xff00;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900449 } else {
450 resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
451 resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900452 }
453
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900454#if defined(__BIG_ENDIAN_BITFIELD)
masakazu.mochizuki.wd@hitachi.com6f107e42016-04-12 17:11:41 +0900455 if (cnt == 4) {
456 cmd->response[0] = (resp[6] << 16) | resp[7];
457 cmd->response[1] = (resp[4] << 16) | resp[5];
458 cmd->response[2] = (resp[2] << 16) | resp[3];
459 cmd->response[3] = (resp[0] << 16) | resp[1];
460 } else {
461 cmd->response[0] = (resp[0] << 16) | resp[1];
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900462 }
463#else
masakazu.mochizuki.wd@hitachi.com6f107e42016-04-12 17:11:41 +0900464 if (cnt == 4) {
465 cmd->response[0] = (resp[7] << 16) | resp[6];
466 cmd->response[1] = (resp[5] << 16) | resp[4];
467 cmd->response[2] = (resp[3] << 16) | resp[2];
468 cmd->response[3] = (resp[1] << 16) | resp[0];
469 } else {
470 cmd->response[0] = (resp[1] << 16) | resp[0];
471 }
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900472#endif /* __BIG_ENDIAN_BITFIELD */
473}
474
475static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
476 struct mmc_data *data, unsigned short opc)
477{
478 switch (opc) {
479 case SD_CMD_APP_SEND_OP_COND:
480 case SD_CMD_APP_SEND_SCR:
481 opc |= SDHI_APP;
482 break;
483 case SD_CMD_APP_SET_BUS_WIDTH:
484 /* SD_APP_SET_BUS_WIDTH*/
485 if (!data)
486 opc |= SDHI_APP;
487 else /* SD_SWITCH */
488 opc = SDHI_SD_SWITCH;
489 break;
Kouei Abe91a16c32017-05-13 15:51:17 +0200490 case MMC_CMD_SEND_OP_COND:
491 opc = SDHI_MMC_SEND_OP_COND;
492 break;
493 case MMC_CMD_SEND_EXT_CSD:
494 if (data)
495 opc = SDHI_MMC_SEND_EXT_CSD;
496 break;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900497 default:
498 break;
499 }
500 return opc;
501}
502
503static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
504 struct mmc_data *data, unsigned short opc)
505{
506 unsigned short ret;
507
508 switch (opc) {
509 case MMC_CMD_READ_MULTIPLE_BLOCK:
510 ret = sh_sdhi_multi_read(host, data);
511 break;
512 case MMC_CMD_WRITE_MULTIPLE_BLOCK:
513 ret = sh_sdhi_multi_write(host, data);
514 break;
515 case MMC_CMD_WRITE_SINGLE_BLOCK:
516 ret = sh_sdhi_single_write(host, data);
517 break;
518 case MMC_CMD_READ_SINGLE_BLOCK:
519 case SDHI_SD_APP_SEND_SCR:
520 case SDHI_SD_SWITCH: /* SD_SWITCH */
Kouei Abe91a16c32017-05-13 15:51:17 +0200521 case SDHI_MMC_SEND_EXT_CSD:
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900522 ret = sh_sdhi_single_read(host, data);
523 break;
524 default:
525 printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
526 ret = -EINVAL;
527 break;
528 }
529 return ret;
530}
531
532static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
533 struct mmc_data *data, struct mmc_cmd *cmd)
534{
535 long time;
536 unsigned short opc = cmd->cmdidx;
537 int ret = 0;
538 unsigned long timeout;
539
540 debug("opc = %d, arg = %x, resp_type = %x\n",
541 opc, cmd->cmdarg, cmd->resp_type);
542
543 if (opc == MMC_CMD_STOP_TRANSMISSION) {
544 /* SDHI sends the STOP command automatically by STOP reg */
545 sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
546 sh_sdhi_readw(host, SDHI_INFO1_MASK));
547
548 time = sh_sdhi_wait_interrupt_flag(host);
549 if (time == 0 || host->sd_error != 0)
550 return sh_sdhi_error_manage(host);
551
552 sh_sdhi_get_response(host, cmd);
553 return 0;
554 }
555
556 if (data) {
557 if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
558 opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
559 sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
560 sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
561 }
562 sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
563 }
564 opc = sh_sdhi_set_cmd(host, data, opc);
565
566 /*
Bin Menga1875592016-02-05 19:30:11 -0800567 * U-Boot cannot use interrupt.
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900568 * So this flag may not be clear by timing
569 */
570 sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
571
572 sh_sdhi_writew(host, SDHI_INFO1_MASK,
573 INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
574 sh_sdhi_writew(host, SDHI_ARG0,
575 (unsigned short)(cmd->cmdarg & ARG0_MASK));
576 sh_sdhi_writew(host, SDHI_ARG1,
577 (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
578
579 timeout = 100000;
580 /* Waiting for SD Bus busy to be cleared */
581 while (timeout--) {
582 if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
583 break;
584 }
585
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900586 host->wait_int = 0;
587 sh_sdhi_writew(host, SDHI_INFO1_MASK,
588 ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
589 sh_sdhi_writew(host, SDHI_INFO2_MASK,
590 ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
591 INFO2M_END_ERROR | INFO2M_TIMEOUT |
592 INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
593 sh_sdhi_readw(host, SDHI_INFO2_MASK));
594
Kouei Abe3ebc62c2017-05-13 15:51:15 +0200595 sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
596
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900597 time = sh_sdhi_wait_interrupt_flag(host);
598 if (!time)
599 return sh_sdhi_error_manage(host);
600
601 if (host->sd_error) {
602 switch (cmd->cmdidx) {
603 case MMC_CMD_ALL_SEND_CID:
604 case MMC_CMD_SELECT_CARD:
605 case SD_CMD_SEND_IF_COND:
606 case MMC_CMD_APP_CMD:
Jaehoon Chung915ffa52016-07-19 16:33:36 +0900607 ret = -ETIMEDOUT;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900608 break;
609 default:
610 debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
611 debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
612 ret = sh_sdhi_error_manage(host);
613 break;
614 }
615 host->sd_error = 0;
616 host->wait_int = 0;
617 return ret;
618 }
619 if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END)
620 return -EINVAL;
621
622 if (host->wait_int) {
623 sh_sdhi_get_response(host, cmd);
624 host->wait_int = 0;
625 }
626 if (data)
627 ret = sh_sdhi_data_trans(host, data, opc);
628
629 debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
630 ret, cmd->response[0], cmd->response[1],
631 cmd->response[2], cmd->response[3]);
632 return ret;
633}
634
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200635static int sh_sdhi_send_cmd_common(struct sh_sdhi_host *host,
636 struct mmc_cmd *cmd, struct mmc_data *data)
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900637{
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900638 host->sd_error = 0;
639
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200640 return sh_sdhi_start_cmd(host, data, cmd);
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900641}
642
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200643static int sh_sdhi_set_ios_common(struct sh_sdhi_host *host, struct mmc *mmc)
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900644{
645 int ret;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900646
647 ret = sh_sdhi_clock_control(host, mmc->clock);
648 if (ret)
Jaehoon Chung07b0b9c2016-12-30 15:30:16 +0900649 return -EINVAL;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900650
Kouei Abe91a16c32017-05-13 15:51:17 +0200651 if (mmc->bus_width == 8)
652 sh_sdhi_writew(host, SDHI_OPTION,
653 OPT_BUS_WIDTH_8 | (~OPT_BUS_WIDTH_M &
654 sh_sdhi_readw(host, SDHI_OPTION)));
655 else if (mmc->bus_width == 4)
656 sh_sdhi_writew(host, SDHI_OPTION,
657 OPT_BUS_WIDTH_4 | (~OPT_BUS_WIDTH_M &
658 sh_sdhi_readw(host, SDHI_OPTION)));
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900659 else
Kouei Abe91a16c32017-05-13 15:51:17 +0200660 sh_sdhi_writew(host, SDHI_OPTION,
661 OPT_BUS_WIDTH_1 | (~OPT_BUS_WIDTH_M &
662 sh_sdhi_readw(host, SDHI_OPTION)));
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900663
664 debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
Jaehoon Chung07b0b9c2016-12-30 15:30:16 +0900665
666 return 0;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900667}
668
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200669static int sh_sdhi_initialize_common(struct sh_sdhi_host *host)
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900670{
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900671 int ret = sh_sdhi_sync_reset(host);
672
673 sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
674
675#if defined(__BIG_ENDIAN_BITFIELD)
676 sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
677#endif
678
679 sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
680 INFO1M_ACCESS_END | INFO1M_CARD_RE |
681 INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
682
683 return ret;
684}
685
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200686#ifndef CONFIG_DM_MMC
687static void *mmc_priv(struct mmc *mmc)
688{
689 return (void *)mmc->priv;
690}
691
692static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
693 struct mmc_data *data)
694{
695 struct sh_sdhi_host *host = mmc_priv(mmc);
696
697 return sh_sdhi_send_cmd_common(host, cmd, data);
698}
699
700static int sh_sdhi_set_ios(struct mmc *mmc)
701{
702 struct sh_sdhi_host *host = mmc_priv(mmc);
703
704 return sh_sdhi_set_ios_common(host, mmc);
705}
706
707static int sh_sdhi_initialize(struct mmc *mmc)
708{
709 struct sh_sdhi_host *host = mmc_priv(mmc);
710
711 return sh_sdhi_initialize_common(host);
712}
713
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900714static const struct mmc_ops sh_sdhi_ops = {
715 .send_cmd = sh_sdhi_send_cmd,
716 .set_ios = sh_sdhi_set_ios,
717 .init = sh_sdhi_initialize,
718};
719
Kouei Abea5950f82017-05-13 15:51:18 +0200720#ifdef CONFIG_RCAR_GEN3
721static struct mmc_config sh_sdhi_cfg = {
722 .name = DRIVER_NAME,
723 .ops = &sh_sdhi_ops,
724 .f_min = CLKDEV_INIT,
725 .f_max = CLKDEV_HS_DATA,
726 .voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
727 .host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS |
728 MMC_MODE_HS_52MHz,
729 .part_type = PART_TYPE_DOS,
730 .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
731};
732#else
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900733static struct mmc_config sh_sdhi_cfg = {
734 .name = DRIVER_NAME,
735 .ops = &sh_sdhi_ops,
736 .f_min = CLKDEV_INIT,
737 .f_max = CLKDEV_HS_DATA,
738 .voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
739 .host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
740 .part_type = PART_TYPE_DOS,
741 .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
742};
Kouei Abea5950f82017-05-13 15:51:18 +0200743#endif
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900744
745int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
746{
747 int ret = 0;
748 struct mmc *mmc;
749 struct sh_sdhi_host *host = NULL;
750
751 if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
752 return -ENODEV;
753
754 host = malloc(sizeof(struct sh_sdhi_host));
755 if (!host)
756 return -ENOMEM;
757
758 mmc = mmc_create(&sh_sdhi_cfg, host);
759 if (!mmc) {
760 ret = -1;
761 goto error;
762 }
763
764 host->ch = ch;
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200765 host->addr = (void __iomem *)addr;
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900766 host->quirks = quirks;
767
Kouei Abe5eada1d2017-05-13 15:51:16 +0200768 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
769 host->bus_shift = 2;
770 else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
Nobuhiro Iwamatsu72d42ba2014-12-17 08:03:00 +0900771 host->bus_shift = 1;
772
773 return ret;
774error:
775 if (host)
776 free(host);
777 return ret;
778}
Marek Vasutd1c18ca2017-07-21 23:22:54 +0200779
780#else
781
782struct sh_sdhi_plat {
783 struct mmc_config cfg;
784 struct mmc mmc;
785};
786
787int sh_sdhi_dm_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
788 struct mmc_data *data)
789{
790 struct sh_sdhi_host *host = dev_get_priv(dev);
791
792 return sh_sdhi_send_cmd_common(host, cmd, data);
793}
794
795int sh_sdhi_dm_set_ios(struct udevice *dev)
796{
797 struct sh_sdhi_host *host = dev_get_priv(dev);
798 struct mmc *mmc = mmc_get_mmc_dev(dev);
799
800 return sh_sdhi_set_ios_common(host, mmc);
801}
802
803static const struct dm_mmc_ops sh_sdhi_dm_ops = {
804 .send_cmd = sh_sdhi_dm_send_cmd,
805 .set_ios = sh_sdhi_dm_set_ios,
806};
807
808static int sh_sdhi_dm_bind(struct udevice *dev)
809{
810 struct sh_sdhi_plat *plat = dev_get_platdata(dev);
811
812 return mmc_bind(dev, &plat->mmc, &plat->cfg);
813}
814
815static int sh_sdhi_dm_probe(struct udevice *dev)
816{
817 struct sh_sdhi_plat *plat = dev_get_platdata(dev);
818 struct sh_sdhi_host *host = dev_get_priv(dev);
819 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
820 const u32 quirks = dev_get_driver_data(dev);
821 fdt_addr_t base;
822
823 base = devfdt_get_addr(dev);
824 if (base == FDT_ADDR_T_NONE)
825 return -EINVAL;
826
827 host->addr = devm_ioremap(dev, base, SZ_2K);
828 if (!host->addr)
829 return -ENOMEM;
830
831 host->quirks = quirks;
832
833 if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
834 host->bus_shift = 2;
835 else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
836 host->bus_shift = 1;
837
838 plat->cfg.name = dev->name;
839 plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
840
841 switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width",
842 1)) {
843 case 8:
844 plat->cfg.host_caps |= MMC_MODE_8BIT;
845 break;
846 case 4:
847 plat->cfg.host_caps |= MMC_MODE_4BIT;
848 break;
849 case 1:
850 break;
851 default:
852 dev_err(dev, "Invalid \"bus-width\" value\n");
853 return -EINVAL;
854 }
855
856 sh_sdhi_initialize_common(host);
857
858 plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
859 plat->cfg.f_min = CLKDEV_INIT;
860 plat->cfg.f_max = CLKDEV_HS_DATA;
861 plat->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
862
863 upriv->mmc = &plat->mmc;
864
865 return 0;
866}
867
868static const struct udevice_id sh_sdhi_sd_match[] = {
869 { .compatible = "renesas,sdhi-r8a7795", .data = SH_SDHI_QUIRK_64BIT_BUF },
870 { .compatible = "renesas,sdhi-r8a7796", .data = SH_SDHI_QUIRK_64BIT_BUF },
871 { /* sentinel */ }
872};
873
874U_BOOT_DRIVER(sh_sdhi_mmc) = {
875 .name = "sh-sdhi-mmc",
876 .id = UCLASS_MMC,
877 .of_match = sh_sdhi_sd_match,
878 .bind = sh_sdhi_dm_bind,
879 .probe = sh_sdhi_dm_probe,
880 .priv_auto_alloc_size = sizeof(struct sh_sdhi_host),
881 .platdata_auto_alloc_size = sizeof(struct sh_sdhi_plat),
882 .ops = &sh_sdhi_dm_ops,
883};
884#endif