xref: /rk3399_rockchip-uboot/drivers/mmc/sh_sdhi.c (revision 1fdafb2e3dfecdc4129a8062ad25b1adb32b0efb)
172d42badSNobuhiro Iwamatsu /*
272d42badSNobuhiro Iwamatsu  * drivers/mmc/sh_sdhi.c
372d42badSNobuhiro Iwamatsu  *
472d42badSNobuhiro Iwamatsu  * SD/MMC driver for Renesas rmobile ARM SoCs.
572d42badSNobuhiro Iwamatsu  *
65eada1dbSKouei Abe  * Copyright (C) 2011,2013-2017 Renesas Electronics Corporation
772d42badSNobuhiro Iwamatsu  * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
872d42badSNobuhiro Iwamatsu  * Copyright (C) 2008-2009 Renesas Solutions Corp.
972d42badSNobuhiro Iwamatsu  *
1072d42badSNobuhiro Iwamatsu  * SPDX-License-Identifier:	GPL-2.0
1172d42badSNobuhiro Iwamatsu  */
1272d42badSNobuhiro Iwamatsu 
1372d42badSNobuhiro Iwamatsu #include <common.h>
1472d42badSNobuhiro Iwamatsu #include <malloc.h>
1572d42badSNobuhiro Iwamatsu #include <mmc.h>
16d1c18ca1SMarek Vasut #include <dm.h>
171221ce45SMasahiro Yamada #include <linux/errno.h>
18d1c18ca1SMarek Vasut #include <linux/compat.h>
19d1c18ca1SMarek Vasut #include <linux/io.h>
20d1c18ca1SMarek Vasut #include <linux/sizes.h>
2172d42badSNobuhiro Iwamatsu #include <asm/arch/rmobile.h>
2272d42badSNobuhiro Iwamatsu #include <asm/arch/sh_sdhi.h>
23*8cd46cbaSMarek Vasut #include <clk.h>
2472d42badSNobuhiro Iwamatsu 
2572d42badSNobuhiro Iwamatsu #define DRIVER_NAME "sh-sdhi"
2672d42badSNobuhiro Iwamatsu 
2772d42badSNobuhiro Iwamatsu struct sh_sdhi_host {
28d1c18ca1SMarek Vasut 	void __iomem *addr;
2972d42badSNobuhiro Iwamatsu 	int ch;
3072d42badSNobuhiro Iwamatsu 	int bus_shift;
3172d42badSNobuhiro Iwamatsu 	unsigned long quirks;
3272d42badSNobuhiro Iwamatsu 	unsigned char wait_int;
3372d42badSNobuhiro Iwamatsu 	unsigned char sd_error;
3472d42badSNobuhiro Iwamatsu 	unsigned char detect_waiting;
35a3f0a7d5SMarek Vasut 	unsigned char app_cmd;
3672d42badSNobuhiro Iwamatsu };
375eada1dbSKouei Abe 
sh_sdhi_writeq(struct sh_sdhi_host * host,int reg,u64 val)385eada1dbSKouei Abe static inline void sh_sdhi_writeq(struct sh_sdhi_host *host, int reg, u64 val)
395eada1dbSKouei Abe {
405eada1dbSKouei Abe 	writeq(val, host->addr + (reg << host->bus_shift));
415eada1dbSKouei Abe }
425eada1dbSKouei Abe 
sh_sdhi_readq(struct sh_sdhi_host * host,int reg)435eada1dbSKouei Abe static inline u64 sh_sdhi_readq(struct sh_sdhi_host *host, int reg)
445eada1dbSKouei Abe {
455eada1dbSKouei Abe 	return readq(host->addr + (reg << host->bus_shift));
465eada1dbSKouei Abe }
475eada1dbSKouei Abe 
sh_sdhi_writew(struct sh_sdhi_host * host,int reg,u16 val)4872d42badSNobuhiro Iwamatsu static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
4972d42badSNobuhiro Iwamatsu {
5072d42badSNobuhiro Iwamatsu 	writew(val, host->addr + (reg << host->bus_shift));
5172d42badSNobuhiro Iwamatsu }
5272d42badSNobuhiro Iwamatsu 
sh_sdhi_readw(struct sh_sdhi_host * host,int reg)5372d42badSNobuhiro Iwamatsu static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
5472d42badSNobuhiro Iwamatsu {
5572d42badSNobuhiro Iwamatsu 	return readw(host->addr + (reg << host->bus_shift));
5672d42badSNobuhiro Iwamatsu }
5772d42badSNobuhiro Iwamatsu 
sh_sdhi_detect(struct sh_sdhi_host * host)5872d42badSNobuhiro Iwamatsu static void sh_sdhi_detect(struct sh_sdhi_host *host)
5972d42badSNobuhiro Iwamatsu {
6072d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_OPTION,
6172d42badSNobuhiro Iwamatsu 		       OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
6272d42badSNobuhiro Iwamatsu 
6372d42badSNobuhiro Iwamatsu 	host->detect_waiting = 0;
6472d42badSNobuhiro Iwamatsu }
6572d42badSNobuhiro Iwamatsu 
sh_sdhi_intr(void * dev_id)6672d42badSNobuhiro Iwamatsu static int sh_sdhi_intr(void *dev_id)
6772d42badSNobuhiro Iwamatsu {
6872d42badSNobuhiro Iwamatsu 	struct sh_sdhi_host *host = dev_id;
6972d42badSNobuhiro Iwamatsu 	int state1 = 0, state2 = 0;
7072d42badSNobuhiro Iwamatsu 
7172d42badSNobuhiro Iwamatsu 	state1 = sh_sdhi_readw(host, SDHI_INFO1);
7272d42badSNobuhiro Iwamatsu 	state2 = sh_sdhi_readw(host, SDHI_INFO2);
7372d42badSNobuhiro Iwamatsu 
7472d42badSNobuhiro Iwamatsu 	debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
7572d42badSNobuhiro Iwamatsu 
7672d42badSNobuhiro Iwamatsu 	/* CARD Insert */
7772d42badSNobuhiro Iwamatsu 	if (state1 & INFO1_CARD_IN) {
7872d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
7972d42badSNobuhiro Iwamatsu 		if (!host->detect_waiting) {
8072d42badSNobuhiro Iwamatsu 			host->detect_waiting = 1;
8172d42badSNobuhiro Iwamatsu 			sh_sdhi_detect(host);
8272d42badSNobuhiro Iwamatsu 		}
8372d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
8472d42badSNobuhiro Iwamatsu 			       INFO1M_ACCESS_END | INFO1M_CARD_IN |
8572d42badSNobuhiro Iwamatsu 			       INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
8672d42badSNobuhiro Iwamatsu 		return -EAGAIN;
8772d42badSNobuhiro Iwamatsu 	}
8872d42badSNobuhiro Iwamatsu 	/* CARD Removal */
8972d42badSNobuhiro Iwamatsu 	if (state1 & INFO1_CARD_RE) {
9072d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
9172d42badSNobuhiro Iwamatsu 		if (!host->detect_waiting) {
9272d42badSNobuhiro Iwamatsu 			host->detect_waiting = 1;
9372d42badSNobuhiro Iwamatsu 			sh_sdhi_detect(host);
9472d42badSNobuhiro Iwamatsu 		}
9572d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
9672d42badSNobuhiro Iwamatsu 			       INFO1M_ACCESS_END | INFO1M_CARD_RE |
9772d42badSNobuhiro Iwamatsu 			       INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
9872d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
9972d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
10072d42badSNobuhiro Iwamatsu 		return -EAGAIN;
10172d42badSNobuhiro Iwamatsu 	}
10272d42badSNobuhiro Iwamatsu 
10372d42badSNobuhiro Iwamatsu 	if (state2 & INFO2_ALL_ERR) {
10472d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2,
10572d42badSNobuhiro Iwamatsu 			       (unsigned short)~(INFO2_ALL_ERR));
10672d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2_MASK,
10772d42badSNobuhiro Iwamatsu 			       INFO2M_ALL_ERR |
10872d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO2_MASK));
10972d42badSNobuhiro Iwamatsu 		host->sd_error = 1;
11072d42badSNobuhiro Iwamatsu 		host->wait_int = 1;
11172d42badSNobuhiro Iwamatsu 		return 0;
11272d42badSNobuhiro Iwamatsu 	}
11372d42badSNobuhiro Iwamatsu 	/* Respons End */
11472d42badSNobuhiro Iwamatsu 	if (state1 & INFO1_RESP_END) {
11572d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
11672d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1_MASK,
11772d42badSNobuhiro Iwamatsu 			       INFO1M_RESP_END |
11872d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO1_MASK));
11972d42badSNobuhiro Iwamatsu 		host->wait_int = 1;
12072d42badSNobuhiro Iwamatsu 		return 0;
12172d42badSNobuhiro Iwamatsu 	}
12272d42badSNobuhiro Iwamatsu 	/* SD_BUF Read Enable */
12372d42badSNobuhiro Iwamatsu 	if (state2 & INFO2_BRE_ENABLE) {
12472d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
12572d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2_MASK,
12672d42badSNobuhiro Iwamatsu 			       INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
12772d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO2_MASK));
12872d42badSNobuhiro Iwamatsu 		host->wait_int = 1;
12972d42badSNobuhiro Iwamatsu 		return 0;
13072d42badSNobuhiro Iwamatsu 	}
13172d42badSNobuhiro Iwamatsu 	/* SD_BUF Write Enable */
13272d42badSNobuhiro Iwamatsu 	if (state2 & INFO2_BWE_ENABLE) {
13372d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
13472d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2_MASK,
13572d42badSNobuhiro Iwamatsu 			       INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
13672d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO2_MASK));
13772d42badSNobuhiro Iwamatsu 		host->wait_int = 1;
13872d42badSNobuhiro Iwamatsu 		return 0;
13972d42badSNobuhiro Iwamatsu 	}
14072d42badSNobuhiro Iwamatsu 	/* Access End */
14172d42badSNobuhiro Iwamatsu 	if (state1 & INFO1_ACCESS_END) {
14272d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
14372d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1_MASK,
14472d42badSNobuhiro Iwamatsu 			       INFO1_ACCESS_END |
14572d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO1_MASK));
14672d42badSNobuhiro Iwamatsu 		host->wait_int = 1;
14772d42badSNobuhiro Iwamatsu 		return 0;
14872d42badSNobuhiro Iwamatsu 	}
14972d42badSNobuhiro Iwamatsu 	return -EAGAIN;
15072d42badSNobuhiro Iwamatsu }
15172d42badSNobuhiro Iwamatsu 
sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host * host)15272d42badSNobuhiro Iwamatsu static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
15372d42badSNobuhiro Iwamatsu {
15472d42badSNobuhiro Iwamatsu 	int timeout = 10000000;
15572d42badSNobuhiro Iwamatsu 
15672d42badSNobuhiro Iwamatsu 	while (1) {
15772d42badSNobuhiro Iwamatsu 		timeout--;
15872d42badSNobuhiro Iwamatsu 		if (timeout < 0) {
15972d42badSNobuhiro Iwamatsu 			debug(DRIVER_NAME": %s timeout\n", __func__);
16072d42badSNobuhiro Iwamatsu 			return 0;
16172d42badSNobuhiro Iwamatsu 		}
16272d42badSNobuhiro Iwamatsu 
16372d42badSNobuhiro Iwamatsu 		if (!sh_sdhi_intr(host))
16472d42badSNobuhiro Iwamatsu 			break;
16572d42badSNobuhiro Iwamatsu 
16672d42badSNobuhiro Iwamatsu 		udelay(1);	/* 1 usec */
16772d42badSNobuhiro Iwamatsu 	}
16872d42badSNobuhiro Iwamatsu 
16972d42badSNobuhiro Iwamatsu 	return 1; /* Return value: NOT 0 = complete waiting */
17072d42badSNobuhiro Iwamatsu }
17172d42badSNobuhiro Iwamatsu 
sh_sdhi_clock_control(struct sh_sdhi_host * host,unsigned long clk)17272d42badSNobuhiro Iwamatsu static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
17372d42badSNobuhiro Iwamatsu {
17472d42badSNobuhiro Iwamatsu 	u32 clkdiv, i, timeout;
17572d42badSNobuhiro Iwamatsu 
17672d42badSNobuhiro Iwamatsu 	if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
17772d42badSNobuhiro Iwamatsu 		printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
17872d42badSNobuhiro Iwamatsu 		return -EBUSY;
17972d42badSNobuhiro Iwamatsu 	}
18072d42badSNobuhiro Iwamatsu 
18172d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_CLK_CTRL,
18272d42badSNobuhiro Iwamatsu 		       ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
18372d42badSNobuhiro Iwamatsu 
18472d42badSNobuhiro Iwamatsu 	if (clk == 0)
18572d42badSNobuhiro Iwamatsu 		return -EIO;
18672d42badSNobuhiro Iwamatsu 
18772d42badSNobuhiro Iwamatsu 	clkdiv = 0x80;
18872d42badSNobuhiro Iwamatsu 	i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
18972d42badSNobuhiro Iwamatsu 	for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
19072d42badSNobuhiro Iwamatsu 		i <<= 1;
19172d42badSNobuhiro Iwamatsu 
19272d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
19372d42badSNobuhiro Iwamatsu 
19472d42badSNobuhiro Iwamatsu 	timeout = 100000;
19572d42badSNobuhiro Iwamatsu 	/* Waiting for SD Bus busy to be cleared */
19672d42badSNobuhiro Iwamatsu 	while (timeout--) {
19772d42badSNobuhiro Iwamatsu 		if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
19872d42badSNobuhiro Iwamatsu 			break;
19972d42badSNobuhiro Iwamatsu 	}
20072d42badSNobuhiro Iwamatsu 
20172d42badSNobuhiro Iwamatsu 	if (timeout)
20272d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_CLK_CTRL,
20372d42badSNobuhiro Iwamatsu 			       CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
20472d42badSNobuhiro Iwamatsu 	else
20572d42badSNobuhiro Iwamatsu 		return -EBUSY;
20672d42badSNobuhiro Iwamatsu 
20772d42badSNobuhiro Iwamatsu 	return 0;
20872d42badSNobuhiro Iwamatsu }
20972d42badSNobuhiro Iwamatsu 
sh_sdhi_sync_reset(struct sh_sdhi_host * host)21072d42badSNobuhiro Iwamatsu static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
21172d42badSNobuhiro Iwamatsu {
21272d42badSNobuhiro Iwamatsu 	u32 timeout;
21372d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
21472d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
21572d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_CLK_CTRL,
21672d42badSNobuhiro Iwamatsu 		       CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
21772d42badSNobuhiro Iwamatsu 
21872d42badSNobuhiro Iwamatsu 	timeout = 100000;
21972d42badSNobuhiro Iwamatsu 	while (timeout--) {
22072d42badSNobuhiro Iwamatsu 		if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
22172d42badSNobuhiro Iwamatsu 			break;
22272d42badSNobuhiro Iwamatsu 		udelay(100);
22372d42badSNobuhiro Iwamatsu 	}
22472d42badSNobuhiro Iwamatsu 
22572d42badSNobuhiro Iwamatsu 	if (!timeout)
22672d42badSNobuhiro Iwamatsu 		return -EBUSY;
22772d42badSNobuhiro Iwamatsu 
22872d42badSNobuhiro Iwamatsu 	if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
22972d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
23072d42badSNobuhiro Iwamatsu 
23172d42badSNobuhiro Iwamatsu 	return 0;
23272d42badSNobuhiro Iwamatsu }
23372d42badSNobuhiro Iwamatsu 
sh_sdhi_error_manage(struct sh_sdhi_host * host)23472d42badSNobuhiro Iwamatsu static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
23572d42badSNobuhiro Iwamatsu {
23672d42badSNobuhiro Iwamatsu 	unsigned short e_state1, e_state2;
23772d42badSNobuhiro Iwamatsu 	int ret;
23872d42badSNobuhiro Iwamatsu 
23972d42badSNobuhiro Iwamatsu 	host->sd_error = 0;
24072d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
24172d42badSNobuhiro Iwamatsu 
24272d42badSNobuhiro Iwamatsu 	e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
24372d42badSNobuhiro Iwamatsu 	e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
24472d42badSNobuhiro Iwamatsu 	if (e_state2 & ERR_STS2_SYS_ERROR) {
24572d42badSNobuhiro Iwamatsu 		if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
246915ffa52SJaehoon Chung 			ret = -ETIMEDOUT;
24772d42badSNobuhiro Iwamatsu 		else
24872d42badSNobuhiro Iwamatsu 			ret = -EILSEQ;
24972d42badSNobuhiro Iwamatsu 		debug("%s: ERR_STS2 = %04x\n",
25072d42badSNobuhiro Iwamatsu 		      DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
25172d42badSNobuhiro Iwamatsu 		sh_sdhi_sync_reset(host);
25272d42badSNobuhiro Iwamatsu 
25372d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1_MASK,
25472d42badSNobuhiro Iwamatsu 			       INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
25572d42badSNobuhiro Iwamatsu 		return ret;
25672d42badSNobuhiro Iwamatsu 	}
25772d42badSNobuhiro Iwamatsu 	if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
25872d42badSNobuhiro Iwamatsu 		ret = -EILSEQ;
25972d42badSNobuhiro Iwamatsu 	else
260915ffa52SJaehoon Chung 		ret = -ETIMEDOUT;
26172d42badSNobuhiro Iwamatsu 
26272d42badSNobuhiro Iwamatsu 	debug("%s: ERR_STS1 = %04x\n",
26372d42badSNobuhiro Iwamatsu 	      DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
26472d42badSNobuhiro Iwamatsu 	sh_sdhi_sync_reset(host);
26572d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1_MASK,
26672d42badSNobuhiro Iwamatsu 		       INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
26772d42badSNobuhiro Iwamatsu 	return ret;
26872d42badSNobuhiro Iwamatsu }
26972d42badSNobuhiro Iwamatsu 
sh_sdhi_single_read(struct sh_sdhi_host * host,struct mmc_data * data)27072d42badSNobuhiro Iwamatsu static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
27172d42badSNobuhiro Iwamatsu {
27272d42badSNobuhiro Iwamatsu 	long time;
27372d42badSNobuhiro Iwamatsu 	unsigned short blocksize, i;
27472d42badSNobuhiro Iwamatsu 	unsigned short *p = (unsigned short *)data->dest;
2755eada1dbSKouei Abe 	u64 *q = (u64 *)data->dest;
27672d42badSNobuhiro Iwamatsu 
27772d42badSNobuhiro Iwamatsu 	if ((unsigned long)p & 0x00000001) {
27872d42badSNobuhiro Iwamatsu 		debug(DRIVER_NAME": %s: The data pointer is unaligned.",
27972d42badSNobuhiro Iwamatsu 		      __func__);
28072d42badSNobuhiro Iwamatsu 		return -EIO;
28172d42badSNobuhiro Iwamatsu 	}
28272d42badSNobuhiro Iwamatsu 
28372d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
28472d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO2_MASK,
28572d42badSNobuhiro Iwamatsu 		       ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
28672d42badSNobuhiro Iwamatsu 		       sh_sdhi_readw(host, SDHI_INFO2_MASK));
28772d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1_MASK,
28872d42badSNobuhiro Iwamatsu 		       ~INFO1M_ACCESS_END &
28972d42badSNobuhiro Iwamatsu 		       sh_sdhi_readw(host, SDHI_INFO1_MASK));
29072d42badSNobuhiro Iwamatsu 	time = sh_sdhi_wait_interrupt_flag(host);
29172d42badSNobuhiro Iwamatsu 	if (time == 0 || host->sd_error != 0)
29272d42badSNobuhiro Iwamatsu 		return sh_sdhi_error_manage(host);
29372d42badSNobuhiro Iwamatsu 
29472d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
29572d42badSNobuhiro Iwamatsu 	blocksize = sh_sdhi_readw(host, SDHI_SIZE);
2965eada1dbSKouei Abe 	if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
2975eada1dbSKouei Abe 		for (i = 0; i < blocksize / 8; i++)
2985eada1dbSKouei Abe 			*q++ = sh_sdhi_readq(host, SDHI_BUF0);
2995eada1dbSKouei Abe 	else
30072d42badSNobuhiro Iwamatsu 		for (i = 0; i < blocksize / 2; i++)
30172d42badSNobuhiro Iwamatsu 			*p++ = sh_sdhi_readw(host, SDHI_BUF0);
30272d42badSNobuhiro Iwamatsu 
30372d42badSNobuhiro Iwamatsu 	time = sh_sdhi_wait_interrupt_flag(host);
30472d42badSNobuhiro Iwamatsu 	if (time == 0 || host->sd_error != 0)
30572d42badSNobuhiro Iwamatsu 		return sh_sdhi_error_manage(host);
30672d42badSNobuhiro Iwamatsu 
30772d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
30872d42badSNobuhiro Iwamatsu 	return 0;
30972d42badSNobuhiro Iwamatsu }
31072d42badSNobuhiro Iwamatsu 
sh_sdhi_multi_read(struct sh_sdhi_host * host,struct mmc_data * data)31172d42badSNobuhiro Iwamatsu static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
31272d42badSNobuhiro Iwamatsu {
31372d42badSNobuhiro Iwamatsu 	long time;
31472d42badSNobuhiro Iwamatsu 	unsigned short blocksize, i, sec;
31572d42badSNobuhiro Iwamatsu 	unsigned short *p = (unsigned short *)data->dest;
3165eada1dbSKouei Abe 	u64 *q = (u64 *)data->dest;
31772d42badSNobuhiro Iwamatsu 
31872d42badSNobuhiro Iwamatsu 	if ((unsigned long)p & 0x00000001) {
31972d42badSNobuhiro Iwamatsu 		debug(DRIVER_NAME": %s: The data pointer is unaligned.",
32072d42badSNobuhiro Iwamatsu 		      __func__);
32172d42badSNobuhiro Iwamatsu 		return -EIO;
32272d42badSNobuhiro Iwamatsu 	}
32372d42badSNobuhiro Iwamatsu 
32472d42badSNobuhiro Iwamatsu 	debug("%s: blocks = %d, blocksize = %d\n",
32572d42badSNobuhiro Iwamatsu 	      __func__, data->blocks, data->blocksize);
32672d42badSNobuhiro Iwamatsu 
32772d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
32872d42badSNobuhiro Iwamatsu 	for (sec = 0; sec < data->blocks; sec++) {
32972d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2_MASK,
33072d42badSNobuhiro Iwamatsu 			       ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
33172d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO2_MASK));
33272d42badSNobuhiro Iwamatsu 
33372d42badSNobuhiro Iwamatsu 		time = sh_sdhi_wait_interrupt_flag(host);
33472d42badSNobuhiro Iwamatsu 		if (time == 0 || host->sd_error != 0)
33572d42badSNobuhiro Iwamatsu 			return sh_sdhi_error_manage(host);
33672d42badSNobuhiro Iwamatsu 
33772d42badSNobuhiro Iwamatsu 		host->wait_int = 0;
33872d42badSNobuhiro Iwamatsu 		blocksize = sh_sdhi_readw(host, SDHI_SIZE);
3395eada1dbSKouei Abe 		if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
3405eada1dbSKouei Abe 			for (i = 0; i < blocksize / 8; i++)
3415eada1dbSKouei Abe 				*q++ = sh_sdhi_readq(host, SDHI_BUF0);
3425eada1dbSKouei Abe 		else
34372d42badSNobuhiro Iwamatsu 			for (i = 0; i < blocksize / 2; i++)
34472d42badSNobuhiro Iwamatsu 				*p++ = sh_sdhi_readw(host, SDHI_BUF0);
34572d42badSNobuhiro Iwamatsu 	}
34672d42badSNobuhiro Iwamatsu 
34772d42badSNobuhiro Iwamatsu 	return 0;
34872d42badSNobuhiro Iwamatsu }
34972d42badSNobuhiro Iwamatsu 
sh_sdhi_single_write(struct sh_sdhi_host * host,struct mmc_data * data)35072d42badSNobuhiro Iwamatsu static int sh_sdhi_single_write(struct sh_sdhi_host *host,
35172d42badSNobuhiro Iwamatsu 		struct mmc_data *data)
35272d42badSNobuhiro Iwamatsu {
35372d42badSNobuhiro Iwamatsu 	long time;
35472d42badSNobuhiro Iwamatsu 	unsigned short blocksize, i;
35572d42badSNobuhiro Iwamatsu 	const unsigned short *p = (const unsigned short *)data->src;
3565eada1dbSKouei Abe 	const u64 *q = (const u64 *)data->src;
35772d42badSNobuhiro Iwamatsu 
35872d42badSNobuhiro Iwamatsu 	if ((unsigned long)p & 0x00000001) {
35972d42badSNobuhiro Iwamatsu 		debug(DRIVER_NAME": %s: The data pointer is unaligned.",
36072d42badSNobuhiro Iwamatsu 		      __func__);
36172d42badSNobuhiro Iwamatsu 		return -EIO;
36272d42badSNobuhiro Iwamatsu 	}
36372d42badSNobuhiro Iwamatsu 
36472d42badSNobuhiro Iwamatsu 	debug("%s: blocks = %d, blocksize = %d\n",
36572d42badSNobuhiro Iwamatsu 	      __func__, data->blocks, data->blocksize);
36672d42badSNobuhiro Iwamatsu 
36772d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
36872d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO2_MASK,
36972d42badSNobuhiro Iwamatsu 		       ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
37072d42badSNobuhiro Iwamatsu 		       sh_sdhi_readw(host, SDHI_INFO2_MASK));
37172d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1_MASK,
37272d42badSNobuhiro Iwamatsu 		       ~INFO1M_ACCESS_END &
37372d42badSNobuhiro Iwamatsu 		       sh_sdhi_readw(host, SDHI_INFO1_MASK));
37472d42badSNobuhiro Iwamatsu 
37572d42badSNobuhiro Iwamatsu 	time = sh_sdhi_wait_interrupt_flag(host);
37672d42badSNobuhiro Iwamatsu 	if (time == 0 || host->sd_error != 0)
37772d42badSNobuhiro Iwamatsu 		return sh_sdhi_error_manage(host);
37872d42badSNobuhiro Iwamatsu 
37972d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
38072d42badSNobuhiro Iwamatsu 	blocksize = sh_sdhi_readw(host, SDHI_SIZE);
3815eada1dbSKouei Abe 	if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
3825eada1dbSKouei Abe 		for (i = 0; i < blocksize / 8; i++)
3835eada1dbSKouei Abe 			sh_sdhi_writeq(host, SDHI_BUF0, *q++);
3845eada1dbSKouei Abe 	else
38572d42badSNobuhiro Iwamatsu 		for (i = 0; i < blocksize / 2; i++)
38672d42badSNobuhiro Iwamatsu 			sh_sdhi_writew(host, SDHI_BUF0, *p++);
38772d42badSNobuhiro Iwamatsu 
38872d42badSNobuhiro Iwamatsu 	time = sh_sdhi_wait_interrupt_flag(host);
38972d42badSNobuhiro Iwamatsu 	if (time == 0 || host->sd_error != 0)
39072d42badSNobuhiro Iwamatsu 		return sh_sdhi_error_manage(host);
39172d42badSNobuhiro Iwamatsu 
39272d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
39372d42badSNobuhiro Iwamatsu 	return 0;
39472d42badSNobuhiro Iwamatsu }
39572d42badSNobuhiro Iwamatsu 
sh_sdhi_multi_write(struct sh_sdhi_host * host,struct mmc_data * data)39672d42badSNobuhiro Iwamatsu static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
39772d42badSNobuhiro Iwamatsu {
39872d42badSNobuhiro Iwamatsu 	long time;
39972d42badSNobuhiro Iwamatsu 	unsigned short i, sec, blocksize;
40072d42badSNobuhiro Iwamatsu 	const unsigned short *p = (const unsigned short *)data->src;
4015eada1dbSKouei Abe 	const u64 *q = (const u64 *)data->src;
40272d42badSNobuhiro Iwamatsu 
40372d42badSNobuhiro Iwamatsu 	debug("%s: blocks = %d, blocksize = %d\n",
40472d42badSNobuhiro Iwamatsu 	      __func__, data->blocks, data->blocksize);
40572d42badSNobuhiro Iwamatsu 
40672d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
40772d42badSNobuhiro Iwamatsu 	for (sec = 0; sec < data->blocks; sec++) {
40872d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO2_MASK,
40972d42badSNobuhiro Iwamatsu 			       ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
41072d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO2_MASK));
41172d42badSNobuhiro Iwamatsu 
41272d42badSNobuhiro Iwamatsu 		time = sh_sdhi_wait_interrupt_flag(host);
41372d42badSNobuhiro Iwamatsu 		if (time == 0 || host->sd_error != 0)
41472d42badSNobuhiro Iwamatsu 			return sh_sdhi_error_manage(host);
41572d42badSNobuhiro Iwamatsu 
41672d42badSNobuhiro Iwamatsu 		host->wait_int = 0;
41772d42badSNobuhiro Iwamatsu 		blocksize = sh_sdhi_readw(host, SDHI_SIZE);
4185eada1dbSKouei Abe 		if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
4195eada1dbSKouei Abe 			for (i = 0; i < blocksize / 8; i++)
4205eada1dbSKouei Abe 				sh_sdhi_writeq(host, SDHI_BUF0, *q++);
4215eada1dbSKouei Abe 		else
42272d42badSNobuhiro Iwamatsu 			for (i = 0; i < blocksize / 2; i++)
42372d42badSNobuhiro Iwamatsu 				sh_sdhi_writew(host, SDHI_BUF0, *p++);
42472d42badSNobuhiro Iwamatsu 	}
42572d42badSNobuhiro Iwamatsu 
42672d42badSNobuhiro Iwamatsu 	return 0;
42772d42badSNobuhiro Iwamatsu }
42872d42badSNobuhiro Iwamatsu 
sh_sdhi_get_response(struct sh_sdhi_host * host,struct mmc_cmd * cmd)42972d42badSNobuhiro Iwamatsu static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
43072d42badSNobuhiro Iwamatsu {
43172d42badSNobuhiro Iwamatsu 	unsigned short i, j, cnt = 1;
43272d42badSNobuhiro Iwamatsu 	unsigned short resp[8];
43372d42badSNobuhiro Iwamatsu 
43472d42badSNobuhiro Iwamatsu 	if (cmd->resp_type & MMC_RSP_136) {
43572d42badSNobuhiro Iwamatsu 		cnt = 4;
43672d42badSNobuhiro Iwamatsu 		resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
43772d42badSNobuhiro Iwamatsu 		resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
43872d42badSNobuhiro Iwamatsu 		resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
43972d42badSNobuhiro Iwamatsu 		resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
44072d42badSNobuhiro Iwamatsu 		resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
44172d42badSNobuhiro Iwamatsu 		resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
44272d42badSNobuhiro Iwamatsu 		resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
44372d42badSNobuhiro Iwamatsu 		resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
44472d42badSNobuhiro Iwamatsu 
44572d42badSNobuhiro Iwamatsu 		/* SDHI REGISTER SPECIFICATION */
44672d42badSNobuhiro Iwamatsu 		for (i = 7, j = 6; i > 0; i--) {
44772d42badSNobuhiro Iwamatsu 			resp[i] = (resp[i] << 8) & 0xff00;
44872d42badSNobuhiro Iwamatsu 			resp[i] |= (resp[j--] >> 8) & 0x00ff;
44972d42badSNobuhiro Iwamatsu 		}
45072d42badSNobuhiro Iwamatsu 		resp[0] = (resp[0] << 8) & 0xff00;
45172d42badSNobuhiro Iwamatsu 	} else {
45272d42badSNobuhiro Iwamatsu 		resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
45372d42badSNobuhiro Iwamatsu 		resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
45472d42badSNobuhiro Iwamatsu 	}
45572d42badSNobuhiro Iwamatsu 
45672d42badSNobuhiro Iwamatsu #if defined(__BIG_ENDIAN_BITFIELD)
4576f107e4cSmasakazu.mochizuki.wd@hitachi.com 	if (cnt == 4) {
4586f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[0] = (resp[6] << 16) | resp[7];
4596f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[1] = (resp[4] << 16) | resp[5];
4606f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[2] = (resp[2] << 16) | resp[3];
4616f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[3] = (resp[0] << 16) | resp[1];
4626f107e4cSmasakazu.mochizuki.wd@hitachi.com 	} else {
4636f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[0] = (resp[0] << 16) | resp[1];
46472d42badSNobuhiro Iwamatsu 	}
46572d42badSNobuhiro Iwamatsu #else
4666f107e4cSmasakazu.mochizuki.wd@hitachi.com 	if (cnt == 4) {
4676f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[0] = (resp[7] << 16) | resp[6];
4686f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[1] = (resp[5] << 16) | resp[4];
4696f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[2] = (resp[3] << 16) | resp[2];
4706f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[3] = (resp[1] << 16) | resp[0];
4716f107e4cSmasakazu.mochizuki.wd@hitachi.com 	} else {
4726f107e4cSmasakazu.mochizuki.wd@hitachi.com 		cmd->response[0] = (resp[1] << 16) | resp[0];
4736f107e4cSmasakazu.mochizuki.wd@hitachi.com 	}
47472d42badSNobuhiro Iwamatsu #endif /* __BIG_ENDIAN_BITFIELD */
47572d42badSNobuhiro Iwamatsu }
47672d42badSNobuhiro Iwamatsu 
sh_sdhi_set_cmd(struct sh_sdhi_host * host,struct mmc_data * data,unsigned short opc)47772d42badSNobuhiro Iwamatsu static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
47872d42badSNobuhiro Iwamatsu 			struct mmc_data *data, unsigned short opc)
47972d42badSNobuhiro Iwamatsu {
480a3f0a7d5SMarek Vasut 	if (host->app_cmd) {
48172d42badSNobuhiro Iwamatsu 		if (!data)
482a3f0a7d5SMarek Vasut 			host->app_cmd = 0;
483a3f0a7d5SMarek Vasut 		return opc | BIT(6);
48472d42badSNobuhiro Iwamatsu 	}
485a3f0a7d5SMarek Vasut 
486a3f0a7d5SMarek Vasut 	switch (opc) {
487a3f0a7d5SMarek Vasut 	case MMC_CMD_SWITCH:
488a3f0a7d5SMarek Vasut 		return opc | (data ? 0x1c00 : 0x40);
489a3f0a7d5SMarek Vasut 	case MMC_CMD_SEND_EXT_CSD:
490a3f0a7d5SMarek Vasut 		return opc | (data ? 0x1c00 : 0);
491a3f0a7d5SMarek Vasut 	case MMC_CMD_SEND_OP_COND:
492a3f0a7d5SMarek Vasut 		return opc | 0x0700;
493a3f0a7d5SMarek Vasut 	case MMC_CMD_APP_CMD:
494a3f0a7d5SMarek Vasut 		host->app_cmd = 1;
495a3f0a7d5SMarek Vasut 	default:
49672d42badSNobuhiro Iwamatsu 		return opc;
49772d42badSNobuhiro Iwamatsu 	}
498a3f0a7d5SMarek Vasut }
49972d42badSNobuhiro Iwamatsu 
sh_sdhi_data_trans(struct sh_sdhi_host * host,struct mmc_data * data,unsigned short opc)50072d42badSNobuhiro Iwamatsu static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
50172d42badSNobuhiro Iwamatsu 			struct mmc_data *data, unsigned short opc)
50272d42badSNobuhiro Iwamatsu {
503a3f0a7d5SMarek Vasut 	if (host->app_cmd) {
504a3f0a7d5SMarek Vasut 		host->app_cmd = 0;
50572d42badSNobuhiro Iwamatsu 		switch (opc) {
506a3f0a7d5SMarek Vasut 		case SD_CMD_APP_SEND_SCR:
507a3f0a7d5SMarek Vasut 		case SD_CMD_APP_SD_STATUS:
508a3f0a7d5SMarek Vasut 			return sh_sdhi_single_read(host, data);
509a3f0a7d5SMarek Vasut 		default:
510a3f0a7d5SMarek Vasut 			printf(DRIVER_NAME": SD: NOT SUPPORT APP CMD = d'%04d\n",
511a3f0a7d5SMarek Vasut 				opc);
512a3f0a7d5SMarek Vasut 			return -EINVAL;
513a3f0a7d5SMarek Vasut 		}
514a3f0a7d5SMarek Vasut 	} else {
515a3f0a7d5SMarek Vasut 		switch (opc) {
51672d42badSNobuhiro Iwamatsu 		case MMC_CMD_WRITE_MULTIPLE_BLOCK:
517a3f0a7d5SMarek Vasut 			return sh_sdhi_multi_write(host, data);
518a3f0a7d5SMarek Vasut 		case MMC_CMD_READ_MULTIPLE_BLOCK:
519a3f0a7d5SMarek Vasut 			return sh_sdhi_multi_read(host, data);
52072d42badSNobuhiro Iwamatsu 		case MMC_CMD_WRITE_SINGLE_BLOCK:
521a3f0a7d5SMarek Vasut 			return sh_sdhi_single_write(host, data);
52272d42badSNobuhiro Iwamatsu 		case MMC_CMD_READ_SINGLE_BLOCK:
523a3f0a7d5SMarek Vasut 		case MMC_CMD_SWITCH:
524a3f0a7d5SMarek Vasut 		case MMC_CMD_SEND_EXT_CSD:;
525a3f0a7d5SMarek Vasut 			return sh_sdhi_single_read(host, data);
52672d42badSNobuhiro Iwamatsu 		default:
52772d42badSNobuhiro Iwamatsu 			printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
528a3f0a7d5SMarek Vasut 			return -EINVAL;
52972d42badSNobuhiro Iwamatsu 		}
530a3f0a7d5SMarek Vasut 	}
53172d42badSNobuhiro Iwamatsu }
53272d42badSNobuhiro Iwamatsu 
sh_sdhi_start_cmd(struct sh_sdhi_host * host,struct mmc_data * data,struct mmc_cmd * cmd)53372d42badSNobuhiro Iwamatsu static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
53472d42badSNobuhiro Iwamatsu 			struct mmc_data *data, struct mmc_cmd *cmd)
53572d42badSNobuhiro Iwamatsu {
53672d42badSNobuhiro Iwamatsu 	long time;
537a3f0a7d5SMarek Vasut 	unsigned short shcmd, opc = cmd->cmdidx;
53872d42badSNobuhiro Iwamatsu 	int ret = 0;
53972d42badSNobuhiro Iwamatsu 	unsigned long timeout;
54072d42badSNobuhiro Iwamatsu 
54172d42badSNobuhiro Iwamatsu 	debug("opc = %d, arg = %x, resp_type = %x\n",
54272d42badSNobuhiro Iwamatsu 	      opc, cmd->cmdarg, cmd->resp_type);
54372d42badSNobuhiro Iwamatsu 
54472d42badSNobuhiro Iwamatsu 	if (opc == MMC_CMD_STOP_TRANSMISSION) {
54572d42badSNobuhiro Iwamatsu 		/* SDHI sends the STOP command automatically by STOP reg */
54672d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
54772d42badSNobuhiro Iwamatsu 			       sh_sdhi_readw(host, SDHI_INFO1_MASK));
54872d42badSNobuhiro Iwamatsu 
54972d42badSNobuhiro Iwamatsu 		time = sh_sdhi_wait_interrupt_flag(host);
55072d42badSNobuhiro Iwamatsu 		if (time == 0 || host->sd_error != 0)
55172d42badSNobuhiro Iwamatsu 			return sh_sdhi_error_manage(host);
55272d42badSNobuhiro Iwamatsu 
55372d42badSNobuhiro Iwamatsu 		sh_sdhi_get_response(host, cmd);
55472d42badSNobuhiro Iwamatsu 		return 0;
55572d42badSNobuhiro Iwamatsu 	}
55672d42badSNobuhiro Iwamatsu 
55772d42badSNobuhiro Iwamatsu 	if (data) {
55872d42badSNobuhiro Iwamatsu 		if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
55972d42badSNobuhiro Iwamatsu 		    opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
56072d42badSNobuhiro Iwamatsu 			sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
56172d42badSNobuhiro Iwamatsu 			sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
56272d42badSNobuhiro Iwamatsu 		}
56372d42badSNobuhiro Iwamatsu 		sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
56472d42badSNobuhiro Iwamatsu 	}
565a3f0a7d5SMarek Vasut 
566a3f0a7d5SMarek Vasut 	shcmd = sh_sdhi_set_cmd(host, data, opc);
56772d42badSNobuhiro Iwamatsu 
56872d42badSNobuhiro Iwamatsu 	/*
569a187559eSBin Meng 	 *  U-Boot cannot use interrupt.
57072d42badSNobuhiro Iwamatsu 	 *  So this flag may not be clear by timing
57172d42badSNobuhiro Iwamatsu 	 */
57272d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
57372d42badSNobuhiro Iwamatsu 
57472d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1_MASK,
57572d42badSNobuhiro Iwamatsu 		       INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
57672d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_ARG0,
57772d42badSNobuhiro Iwamatsu 		       (unsigned short)(cmd->cmdarg & ARG0_MASK));
57872d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_ARG1,
57972d42badSNobuhiro Iwamatsu 		       (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
58072d42badSNobuhiro Iwamatsu 
58172d42badSNobuhiro Iwamatsu 	timeout = 100000;
58272d42badSNobuhiro Iwamatsu 	/* Waiting for SD Bus busy to be cleared */
58372d42badSNobuhiro Iwamatsu 	while (timeout--) {
58472d42badSNobuhiro Iwamatsu 		if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
58572d42badSNobuhiro Iwamatsu 			break;
58672d42badSNobuhiro Iwamatsu 	}
58772d42badSNobuhiro Iwamatsu 
58872d42badSNobuhiro Iwamatsu 	host->wait_int = 0;
58972d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1_MASK,
59072d42badSNobuhiro Iwamatsu 		       ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
59172d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO2_MASK,
59272d42badSNobuhiro Iwamatsu 		       ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
59372d42badSNobuhiro Iwamatsu 		       INFO2M_END_ERROR | INFO2M_TIMEOUT |
59472d42badSNobuhiro Iwamatsu 		       INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
59572d42badSNobuhiro Iwamatsu 		       sh_sdhi_readw(host, SDHI_INFO2_MASK));
59672d42badSNobuhiro Iwamatsu 
597a3f0a7d5SMarek Vasut 	sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(shcmd & CMD_MASK));
59872d42badSNobuhiro Iwamatsu 	time = sh_sdhi_wait_interrupt_flag(host);
599a3f0a7d5SMarek Vasut 	if (!time) {
600a3f0a7d5SMarek Vasut 		host->app_cmd = 0;
60172d42badSNobuhiro Iwamatsu 		return sh_sdhi_error_manage(host);
602a3f0a7d5SMarek Vasut 	}
60372d42badSNobuhiro Iwamatsu 
60472d42badSNobuhiro Iwamatsu 	if (host->sd_error) {
60572d42badSNobuhiro Iwamatsu 		switch (cmd->cmdidx) {
60672d42badSNobuhiro Iwamatsu 		case MMC_CMD_ALL_SEND_CID:
60772d42badSNobuhiro Iwamatsu 		case MMC_CMD_SELECT_CARD:
60872d42badSNobuhiro Iwamatsu 		case SD_CMD_SEND_IF_COND:
60972d42badSNobuhiro Iwamatsu 		case MMC_CMD_APP_CMD:
610915ffa52SJaehoon Chung 			ret = -ETIMEDOUT;
61172d42badSNobuhiro Iwamatsu 			break;
61272d42badSNobuhiro Iwamatsu 		default:
61372d42badSNobuhiro Iwamatsu 			debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
61472d42badSNobuhiro Iwamatsu 			debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
61572d42badSNobuhiro Iwamatsu 			ret = sh_sdhi_error_manage(host);
61672d42badSNobuhiro Iwamatsu 			break;
61772d42badSNobuhiro Iwamatsu 		}
61872d42badSNobuhiro Iwamatsu 		host->sd_error = 0;
61972d42badSNobuhiro Iwamatsu 		host->wait_int = 0;
620a3f0a7d5SMarek Vasut 		host->app_cmd = 0;
62172d42badSNobuhiro Iwamatsu 		return ret;
62272d42badSNobuhiro Iwamatsu 	}
623a3f0a7d5SMarek Vasut 
624a3f0a7d5SMarek Vasut 	if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END) {
625a3f0a7d5SMarek Vasut 		host->app_cmd = 0;
62672d42badSNobuhiro Iwamatsu 		return -EINVAL;
627a3f0a7d5SMarek Vasut 	}
62872d42badSNobuhiro Iwamatsu 
62972d42badSNobuhiro Iwamatsu 	if (host->wait_int) {
63072d42badSNobuhiro Iwamatsu 		sh_sdhi_get_response(host, cmd);
63172d42badSNobuhiro Iwamatsu 		host->wait_int = 0;
63272d42badSNobuhiro Iwamatsu 	}
633a3f0a7d5SMarek Vasut 
63472d42badSNobuhiro Iwamatsu 	if (data)
63572d42badSNobuhiro Iwamatsu 		ret = sh_sdhi_data_trans(host, data, opc);
63672d42badSNobuhiro Iwamatsu 
63772d42badSNobuhiro Iwamatsu 	debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
63872d42badSNobuhiro Iwamatsu 	      ret, cmd->response[0], cmd->response[1],
63972d42badSNobuhiro Iwamatsu 	      cmd->response[2], cmd->response[3]);
64072d42badSNobuhiro Iwamatsu 	return ret;
64172d42badSNobuhiro Iwamatsu }
64272d42badSNobuhiro Iwamatsu 
sh_sdhi_send_cmd_common(struct sh_sdhi_host * host,struct mmc_cmd * cmd,struct mmc_data * data)643d1c18ca1SMarek Vasut static int sh_sdhi_send_cmd_common(struct sh_sdhi_host *host,
644d1c18ca1SMarek Vasut 				   struct mmc_cmd *cmd, struct mmc_data *data)
64572d42badSNobuhiro Iwamatsu {
64672d42badSNobuhiro Iwamatsu 	host->sd_error = 0;
64772d42badSNobuhiro Iwamatsu 
648d1c18ca1SMarek Vasut 	return sh_sdhi_start_cmd(host, data, cmd);
64972d42badSNobuhiro Iwamatsu }
65072d42badSNobuhiro Iwamatsu 
sh_sdhi_set_ios_common(struct sh_sdhi_host * host,struct mmc * mmc)651d1c18ca1SMarek Vasut static int sh_sdhi_set_ios_common(struct sh_sdhi_host *host, struct mmc *mmc)
65272d42badSNobuhiro Iwamatsu {
65372d42badSNobuhiro Iwamatsu 	int ret;
65472d42badSNobuhiro Iwamatsu 
65572d42badSNobuhiro Iwamatsu 	ret = sh_sdhi_clock_control(host, mmc->clock);
65672d42badSNobuhiro Iwamatsu 	if (ret)
65707b0b9c0SJaehoon Chung 		return -EINVAL;
65872d42badSNobuhiro Iwamatsu 
65991a16c3bSKouei Abe 	if (mmc->bus_width == 8)
66091a16c3bSKouei Abe 		sh_sdhi_writew(host, SDHI_OPTION,
66191a16c3bSKouei Abe 			       OPT_BUS_WIDTH_8 | (~OPT_BUS_WIDTH_M &
66291a16c3bSKouei Abe 			       sh_sdhi_readw(host, SDHI_OPTION)));
66391a16c3bSKouei Abe 	else if (mmc->bus_width == 4)
66491a16c3bSKouei Abe 		sh_sdhi_writew(host, SDHI_OPTION,
66591a16c3bSKouei Abe 			       OPT_BUS_WIDTH_4 | (~OPT_BUS_WIDTH_M &
66691a16c3bSKouei Abe 			       sh_sdhi_readw(host, SDHI_OPTION)));
66772d42badSNobuhiro Iwamatsu 	else
66891a16c3bSKouei Abe 		sh_sdhi_writew(host, SDHI_OPTION,
66991a16c3bSKouei Abe 			       OPT_BUS_WIDTH_1 | (~OPT_BUS_WIDTH_M &
67091a16c3bSKouei Abe 			       sh_sdhi_readw(host, SDHI_OPTION)));
67172d42badSNobuhiro Iwamatsu 
67272d42badSNobuhiro Iwamatsu 	debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
67307b0b9c0SJaehoon Chung 
67407b0b9c0SJaehoon Chung 	return 0;
67572d42badSNobuhiro Iwamatsu }
67672d42badSNobuhiro Iwamatsu 
sh_sdhi_initialize_common(struct sh_sdhi_host * host)677d1c18ca1SMarek Vasut static int sh_sdhi_initialize_common(struct sh_sdhi_host *host)
67872d42badSNobuhiro Iwamatsu {
67972d42badSNobuhiro Iwamatsu 	int ret = sh_sdhi_sync_reset(host);
68072d42badSNobuhiro Iwamatsu 
68172d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
68272d42badSNobuhiro Iwamatsu 
68372d42badSNobuhiro Iwamatsu #if defined(__BIG_ENDIAN_BITFIELD)
68472d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
68572d42badSNobuhiro Iwamatsu #endif
68672d42badSNobuhiro Iwamatsu 
68772d42badSNobuhiro Iwamatsu 	sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
68872d42badSNobuhiro Iwamatsu 		       INFO1M_ACCESS_END | INFO1M_CARD_RE |
68972d42badSNobuhiro Iwamatsu 		       INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
69072d42badSNobuhiro Iwamatsu 
69172d42badSNobuhiro Iwamatsu 	return ret;
69272d42badSNobuhiro Iwamatsu }
69372d42badSNobuhiro Iwamatsu 
694d1c18ca1SMarek Vasut #ifndef CONFIG_DM_MMC
mmc_priv(struct mmc * mmc)695d1c18ca1SMarek Vasut static void *mmc_priv(struct mmc *mmc)
696d1c18ca1SMarek Vasut {
697d1c18ca1SMarek Vasut 	return (void *)mmc->priv;
698d1c18ca1SMarek Vasut }
699d1c18ca1SMarek Vasut 
sh_sdhi_send_cmd(struct mmc * mmc,struct mmc_cmd * cmd,struct mmc_data * data)700d1c18ca1SMarek Vasut static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
701d1c18ca1SMarek Vasut 			    struct mmc_data *data)
702d1c18ca1SMarek Vasut {
703d1c18ca1SMarek Vasut 	struct sh_sdhi_host *host = mmc_priv(mmc);
704d1c18ca1SMarek Vasut 
705d1c18ca1SMarek Vasut 	return sh_sdhi_send_cmd_common(host, cmd, data);
706d1c18ca1SMarek Vasut }
707d1c18ca1SMarek Vasut 
sh_sdhi_set_ios(struct mmc * mmc)708d1c18ca1SMarek Vasut static int sh_sdhi_set_ios(struct mmc *mmc)
709d1c18ca1SMarek Vasut {
710d1c18ca1SMarek Vasut 	struct sh_sdhi_host *host = mmc_priv(mmc);
711d1c18ca1SMarek Vasut 
712d1c18ca1SMarek Vasut 	return sh_sdhi_set_ios_common(host, mmc);
713d1c18ca1SMarek Vasut }
714d1c18ca1SMarek Vasut 
sh_sdhi_initialize(struct mmc * mmc)715d1c18ca1SMarek Vasut static int sh_sdhi_initialize(struct mmc *mmc)
716d1c18ca1SMarek Vasut {
717d1c18ca1SMarek Vasut 	struct sh_sdhi_host *host = mmc_priv(mmc);
718d1c18ca1SMarek Vasut 
719d1c18ca1SMarek Vasut 	return sh_sdhi_initialize_common(host);
720d1c18ca1SMarek Vasut }
721d1c18ca1SMarek Vasut 
72272d42badSNobuhiro Iwamatsu static const struct mmc_ops sh_sdhi_ops = {
72372d42badSNobuhiro Iwamatsu 	.send_cmd       = sh_sdhi_send_cmd,
72472d42badSNobuhiro Iwamatsu 	.set_ios        = sh_sdhi_set_ios,
72572d42badSNobuhiro Iwamatsu 	.init           = sh_sdhi_initialize,
72672d42badSNobuhiro Iwamatsu };
72772d42badSNobuhiro Iwamatsu 
728a5950f8dSKouei Abe #ifdef CONFIG_RCAR_GEN3
729a5950f8dSKouei Abe static struct mmc_config sh_sdhi_cfg = {
730a5950f8dSKouei Abe 	.name           = DRIVER_NAME,
731a5950f8dSKouei Abe 	.ops            = &sh_sdhi_ops,
732a5950f8dSKouei Abe 	.f_min          = CLKDEV_INIT,
733a5950f8dSKouei Abe 	.f_max          = CLKDEV_HS_DATA,
734a5950f8dSKouei Abe 	.voltages       = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
735a5950f8dSKouei Abe 	.host_caps      = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS |
736a5950f8dSKouei Abe 			  MMC_MODE_HS_52MHz,
737a5950f8dSKouei Abe 	.part_type      = PART_TYPE_DOS,
738a5950f8dSKouei Abe 	.b_max          = CONFIG_SYS_MMC_MAX_BLK_COUNT,
739a5950f8dSKouei Abe };
740a5950f8dSKouei Abe #else
74172d42badSNobuhiro Iwamatsu static struct mmc_config sh_sdhi_cfg = {
74272d42badSNobuhiro Iwamatsu 	.name           = DRIVER_NAME,
74372d42badSNobuhiro Iwamatsu 	.ops            = &sh_sdhi_ops,
74472d42badSNobuhiro Iwamatsu 	.f_min          = CLKDEV_INIT,
74572d42badSNobuhiro Iwamatsu 	.f_max          = CLKDEV_HS_DATA,
74672d42badSNobuhiro Iwamatsu 	.voltages       = MMC_VDD_32_33 | MMC_VDD_33_34,
74772d42badSNobuhiro Iwamatsu 	.host_caps      = MMC_MODE_4BIT | MMC_MODE_HS,
74872d42badSNobuhiro Iwamatsu 	.part_type      = PART_TYPE_DOS,
74972d42badSNobuhiro Iwamatsu 	.b_max          = CONFIG_SYS_MMC_MAX_BLK_COUNT,
75072d42badSNobuhiro Iwamatsu };
751a5950f8dSKouei Abe #endif
75272d42badSNobuhiro Iwamatsu 
sh_sdhi_init(unsigned long addr,int ch,unsigned long quirks)75372d42badSNobuhiro Iwamatsu int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
75472d42badSNobuhiro Iwamatsu {
75572d42badSNobuhiro Iwamatsu 	int ret = 0;
75672d42badSNobuhiro Iwamatsu 	struct mmc *mmc;
75772d42badSNobuhiro Iwamatsu 	struct sh_sdhi_host *host = NULL;
75872d42badSNobuhiro Iwamatsu 
75972d42badSNobuhiro Iwamatsu 	if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
76072d42badSNobuhiro Iwamatsu 		return -ENODEV;
76172d42badSNobuhiro Iwamatsu 
76272d42badSNobuhiro Iwamatsu 	host = malloc(sizeof(struct sh_sdhi_host));
76372d42badSNobuhiro Iwamatsu 	if (!host)
76472d42badSNobuhiro Iwamatsu 		return -ENOMEM;
76572d42badSNobuhiro Iwamatsu 
76672d42badSNobuhiro Iwamatsu 	mmc = mmc_create(&sh_sdhi_cfg, host);
76772d42badSNobuhiro Iwamatsu 	if (!mmc) {
76872d42badSNobuhiro Iwamatsu 		ret = -1;
76972d42badSNobuhiro Iwamatsu 		goto error;
77072d42badSNobuhiro Iwamatsu 	}
77172d42badSNobuhiro Iwamatsu 
77272d42badSNobuhiro Iwamatsu 	host->ch = ch;
773d1c18ca1SMarek Vasut 	host->addr = (void __iomem *)addr;
77472d42badSNobuhiro Iwamatsu 	host->quirks = quirks;
77572d42badSNobuhiro Iwamatsu 
7765eada1dbSKouei Abe 	if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
7775eada1dbSKouei Abe 		host->bus_shift = 2;
7785eada1dbSKouei Abe 	else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
77972d42badSNobuhiro Iwamatsu 		host->bus_shift = 1;
78072d42badSNobuhiro Iwamatsu 
78172d42badSNobuhiro Iwamatsu 	return ret;
78272d42badSNobuhiro Iwamatsu error:
78372d42badSNobuhiro Iwamatsu 	if (host)
78472d42badSNobuhiro Iwamatsu 		free(host);
78572d42badSNobuhiro Iwamatsu 	return ret;
78672d42badSNobuhiro Iwamatsu }
787d1c18ca1SMarek Vasut 
788d1c18ca1SMarek Vasut #else
789d1c18ca1SMarek Vasut 
790d1c18ca1SMarek Vasut struct sh_sdhi_plat {
791d1c18ca1SMarek Vasut 	struct mmc_config cfg;
792d1c18ca1SMarek Vasut 	struct mmc mmc;
793d1c18ca1SMarek Vasut };
794d1c18ca1SMarek Vasut 
sh_sdhi_dm_send_cmd(struct udevice * dev,struct mmc_cmd * cmd,struct mmc_data * data)795d1c18ca1SMarek Vasut int sh_sdhi_dm_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
796d1c18ca1SMarek Vasut 			struct mmc_data *data)
797d1c18ca1SMarek Vasut {
798d1c18ca1SMarek Vasut 	struct sh_sdhi_host *host = dev_get_priv(dev);
799d1c18ca1SMarek Vasut 
800d1c18ca1SMarek Vasut 	return sh_sdhi_send_cmd_common(host, cmd, data);
801d1c18ca1SMarek Vasut }
802d1c18ca1SMarek Vasut 
sh_sdhi_dm_set_ios(struct udevice * dev)803d1c18ca1SMarek Vasut int sh_sdhi_dm_set_ios(struct udevice *dev)
804d1c18ca1SMarek Vasut {
805d1c18ca1SMarek Vasut 	struct sh_sdhi_host *host = dev_get_priv(dev);
806d1c18ca1SMarek Vasut 	struct mmc *mmc = mmc_get_mmc_dev(dev);
807d1c18ca1SMarek Vasut 
808d1c18ca1SMarek Vasut 	return sh_sdhi_set_ios_common(host, mmc);
809d1c18ca1SMarek Vasut }
810d1c18ca1SMarek Vasut 
811d1c18ca1SMarek Vasut static const struct dm_mmc_ops sh_sdhi_dm_ops = {
812d1c18ca1SMarek Vasut 	.send_cmd	= sh_sdhi_dm_send_cmd,
813d1c18ca1SMarek Vasut 	.set_ios	= sh_sdhi_dm_set_ios,
814d1c18ca1SMarek Vasut };
815d1c18ca1SMarek Vasut 
sh_sdhi_dm_bind(struct udevice * dev)816d1c18ca1SMarek Vasut static int sh_sdhi_dm_bind(struct udevice *dev)
817d1c18ca1SMarek Vasut {
818d1c18ca1SMarek Vasut 	struct sh_sdhi_plat *plat = dev_get_platdata(dev);
819d1c18ca1SMarek Vasut 
820d1c18ca1SMarek Vasut 	return mmc_bind(dev, &plat->mmc, &plat->cfg);
821d1c18ca1SMarek Vasut }
822d1c18ca1SMarek Vasut 
sh_sdhi_dm_probe(struct udevice * dev)823d1c18ca1SMarek Vasut static int sh_sdhi_dm_probe(struct udevice *dev)
824d1c18ca1SMarek Vasut {
825d1c18ca1SMarek Vasut 	struct sh_sdhi_plat *plat = dev_get_platdata(dev);
826d1c18ca1SMarek Vasut 	struct sh_sdhi_host *host = dev_get_priv(dev);
827d1c18ca1SMarek Vasut 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
828*8cd46cbaSMarek Vasut 	struct clk sh_sdhi_clk;
829d1c18ca1SMarek Vasut 	const u32 quirks = dev_get_driver_data(dev);
830d1c18ca1SMarek Vasut 	fdt_addr_t base;
831*8cd46cbaSMarek Vasut 	int ret;
832d1c18ca1SMarek Vasut 
833d1c18ca1SMarek Vasut 	base = devfdt_get_addr(dev);
834d1c18ca1SMarek Vasut 	if (base == FDT_ADDR_T_NONE)
835d1c18ca1SMarek Vasut 		return -EINVAL;
836d1c18ca1SMarek Vasut 
837d1c18ca1SMarek Vasut 	host->addr = devm_ioremap(dev, base, SZ_2K);
838d1c18ca1SMarek Vasut 	if (!host->addr)
839d1c18ca1SMarek Vasut 		return -ENOMEM;
840d1c18ca1SMarek Vasut 
841*8cd46cbaSMarek Vasut 	ret = clk_get_by_index(dev, 0, &sh_sdhi_clk);
842*8cd46cbaSMarek Vasut 	if (ret) {
843*8cd46cbaSMarek Vasut 		debug("failed to get clock, ret=%d\n", ret);
844*8cd46cbaSMarek Vasut 		return ret;
845*8cd46cbaSMarek Vasut 	}
846*8cd46cbaSMarek Vasut 
847*8cd46cbaSMarek Vasut 	ret = clk_enable(&sh_sdhi_clk);
848*8cd46cbaSMarek Vasut 	if (ret) {
849*8cd46cbaSMarek Vasut 		debug("failed to enable clock, ret=%d\n", ret);
850*8cd46cbaSMarek Vasut 		return ret;
851*8cd46cbaSMarek Vasut 	}
852*8cd46cbaSMarek Vasut 
853d1c18ca1SMarek Vasut 	host->quirks = quirks;
854d1c18ca1SMarek Vasut 
855d1c18ca1SMarek Vasut 	if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
856d1c18ca1SMarek Vasut 		host->bus_shift = 2;
857d1c18ca1SMarek Vasut 	else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
858d1c18ca1SMarek Vasut 		host->bus_shift = 1;
859d1c18ca1SMarek Vasut 
860d1c18ca1SMarek Vasut 	plat->cfg.name = dev->name;
861d1c18ca1SMarek Vasut 	plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
862d1c18ca1SMarek Vasut 
863d1c18ca1SMarek Vasut 	switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width",
864d1c18ca1SMarek Vasut 			       1)) {
865d1c18ca1SMarek Vasut 	case 8:
866d1c18ca1SMarek Vasut 		plat->cfg.host_caps |= MMC_MODE_8BIT;
867d1c18ca1SMarek Vasut 		break;
868d1c18ca1SMarek Vasut 	case 4:
869d1c18ca1SMarek Vasut 		plat->cfg.host_caps |= MMC_MODE_4BIT;
870d1c18ca1SMarek Vasut 		break;
871d1c18ca1SMarek Vasut 	case 1:
872d1c18ca1SMarek Vasut 		break;
873d1c18ca1SMarek Vasut 	default:
874d1c18ca1SMarek Vasut 		dev_err(dev, "Invalid \"bus-width\" value\n");
875d1c18ca1SMarek Vasut 		return -EINVAL;
876d1c18ca1SMarek Vasut 	}
877d1c18ca1SMarek Vasut 
878d1c18ca1SMarek Vasut 	sh_sdhi_initialize_common(host);
879d1c18ca1SMarek Vasut 
880d1c18ca1SMarek Vasut 	plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
881d1c18ca1SMarek Vasut 	plat->cfg.f_min = CLKDEV_INIT;
882d1c18ca1SMarek Vasut 	plat->cfg.f_max = CLKDEV_HS_DATA;
883d1c18ca1SMarek Vasut 	plat->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
884d1c18ca1SMarek Vasut 
885d1c18ca1SMarek Vasut 	upriv->mmc = &plat->mmc;
886d1c18ca1SMarek Vasut 
887d1c18ca1SMarek Vasut 	return 0;
888d1c18ca1SMarek Vasut }
889d1c18ca1SMarek Vasut 
890d1c18ca1SMarek Vasut static const struct udevice_id sh_sdhi_sd_match[] = {
891d1c18ca1SMarek Vasut 	{ .compatible = "renesas,sdhi-r8a7795", .data = SH_SDHI_QUIRK_64BIT_BUF },
892d1c18ca1SMarek Vasut 	{ .compatible = "renesas,sdhi-r8a7796", .data = SH_SDHI_QUIRK_64BIT_BUF },
893d1c18ca1SMarek Vasut 	{ /* sentinel */ }
894d1c18ca1SMarek Vasut };
895d1c18ca1SMarek Vasut 
896d1c18ca1SMarek Vasut U_BOOT_DRIVER(sh_sdhi_mmc) = {
897d1c18ca1SMarek Vasut 	.name			= "sh-sdhi-mmc",
898d1c18ca1SMarek Vasut 	.id			= UCLASS_MMC,
899d1c18ca1SMarek Vasut 	.of_match		= sh_sdhi_sd_match,
900d1c18ca1SMarek Vasut 	.bind			= sh_sdhi_dm_bind,
901d1c18ca1SMarek Vasut 	.probe			= sh_sdhi_dm_probe,
902d1c18ca1SMarek Vasut 	.priv_auto_alloc_size	= sizeof(struct sh_sdhi_host),
903d1c18ca1SMarek Vasut 	.platdata_auto_alloc_size = sizeof(struct sh_sdhi_plat),
904d1c18ca1SMarek Vasut 	.ops			= &sh_sdhi_dm_ops,
905d1c18ca1SMarek Vasut };
906d1c18ca1SMarek Vasut #endif
907