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