xref: /rk3399_ARM-atf/drivers/rpi3/sdhost/rpi3_sdhost.c (revision 2c8ef2ae6b8103da5b6b82b2252877d61c2522bf)
10503adf4SYing-Chun Liu (PaulLiu) /*
20503adf4SYing-Chun Liu (PaulLiu)  * Copyright (c) 2019, Linaro Limited
30503adf4SYing-Chun Liu (PaulLiu)  * Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
40503adf4SYing-Chun Liu (PaulLiu)  *
50503adf4SYing-Chun Liu (PaulLiu)  * SPDX-License-Identifier: BSD-3-Clause
60503adf4SYing-Chun Liu (PaulLiu)  */
70503adf4SYing-Chun Liu (PaulLiu) 
80503adf4SYing-Chun Liu (PaulLiu) #include <arch.h>
90503adf4SYing-Chun Liu (PaulLiu) #include <arch_helpers.h>
100503adf4SYing-Chun Liu (PaulLiu) #include <assert.h>
110503adf4SYing-Chun Liu (PaulLiu) #include <common/debug.h>
120503adf4SYing-Chun Liu (PaulLiu) #include <lib/mmio.h>
130503adf4SYing-Chun Liu (PaulLiu) #include <drivers/delay_timer.h>
140503adf4SYing-Chun Liu (PaulLiu) #include <drivers/rpi3/sdhost/rpi3_sdhost.h>
150503adf4SYing-Chun Liu (PaulLiu) #include <drivers/mmc.h>
160503adf4SYing-Chun Liu (PaulLiu) #include <drivers/rpi3/gpio/rpi3_gpio.h>
170503adf4SYing-Chun Liu (PaulLiu) #include <errno.h>
180503adf4SYing-Chun Liu (PaulLiu) #include <string.h>
190503adf4SYing-Chun Liu (PaulLiu) 
200503adf4SYing-Chun Liu (PaulLiu) static void rpi3_sdhost_initialize(void);
210503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd);
220503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width);
230503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size);
240503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size);
250503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size);
260503adf4SYing-Chun Liu (PaulLiu) 
270503adf4SYing-Chun Liu (PaulLiu) static const struct mmc_ops rpi3_sdhost_ops = {
280503adf4SYing-Chun Liu (PaulLiu) 	.init		= rpi3_sdhost_initialize,
290503adf4SYing-Chun Liu (PaulLiu) 	.send_cmd	= rpi3_sdhost_send_cmd,
300503adf4SYing-Chun Liu (PaulLiu) 	.set_ios	= rpi3_sdhost_set_ios,
310503adf4SYing-Chun Liu (PaulLiu) 	.prepare	= rpi3_sdhost_prepare,
320503adf4SYing-Chun Liu (PaulLiu) 	.read		= rpi3_sdhost_read,
330503adf4SYing-Chun Liu (PaulLiu) 	.write		= rpi3_sdhost_write,
340503adf4SYing-Chun Liu (PaulLiu) };
350503adf4SYing-Chun Liu (PaulLiu) 
360503adf4SYing-Chun Liu (PaulLiu) static struct rpi3_sdhost_params rpi3_sdhost_params;
370503adf4SYing-Chun Liu (PaulLiu) 
380503adf4SYing-Chun Liu (PaulLiu) /**
390503adf4SYing-Chun Liu (PaulLiu)  * Wait for command being processed.
400503adf4SYing-Chun Liu (PaulLiu)  *
410503adf4SYing-Chun Liu (PaulLiu)  * This function waits the command being processed. It compares
420503adf4SYing-Chun Liu (PaulLiu)  * the ENABLE flag of the HC_COMMAND register. When ENABLE flag disappeared
430503adf4SYing-Chun Liu (PaulLiu)  * it means the command is processed by the SDHOST.
440503adf4SYing-Chun Liu (PaulLiu)  * The timeout is currently 1000*100 us = 100 ms.
450503adf4SYing-Chun Liu (PaulLiu)  *
460503adf4SYing-Chun Liu (PaulLiu)  * @return 0: command finished. 1: command timed out.
470503adf4SYing-Chun Liu (PaulLiu)  */
480503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_waitcommand(void)
490503adf4SYing-Chun Liu (PaulLiu) {
500503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
510503adf4SYing-Chun Liu (PaulLiu) 
520503adf4SYing-Chun Liu (PaulLiu) 	volatile int timeout = 1000;
530503adf4SYing-Chun Liu (PaulLiu) 
540503adf4SYing-Chun Liu (PaulLiu) 	while ((mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_ENABLE)
550503adf4SYing-Chun Liu (PaulLiu) 	       && (--timeout > 0)) {
560503adf4SYing-Chun Liu (PaulLiu) 		udelay(100);
570503adf4SYing-Chun Liu (PaulLiu) 	}
580503adf4SYing-Chun Liu (PaulLiu) 
590503adf4SYing-Chun Liu (PaulLiu) 	return ((timeout > 0) ? 0 : (-(ETIMEDOUT)));
600503adf4SYing-Chun Liu (PaulLiu) }
610503adf4SYing-Chun Liu (PaulLiu) 
620503adf4SYing-Chun Liu (PaulLiu) /**
630503adf4SYing-Chun Liu (PaulLiu)  * Send the command and argument to the SDHOST
640503adf4SYing-Chun Liu (PaulLiu)  *
650503adf4SYing-Chun Liu (PaulLiu)  * This function will wait for the previous command finished. And then
660503adf4SYing-Chun Liu (PaulLiu)  * clear any error status of previous command. And then
670503adf4SYing-Chun Liu (PaulLiu)  * send out the command and args. The command will be turned on the ENABLE
680503adf4SYing-Chun Liu (PaulLiu)  * flag before sending out.
690503adf4SYing-Chun Liu (PaulLiu)  */
700503adf4SYing-Chun Liu (PaulLiu) static void send_command_raw(unsigned int cmd, unsigned int arg)
710503adf4SYing-Chun Liu (PaulLiu) {
720503adf4SYing-Chun Liu (PaulLiu) 	unsigned int status;
730503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
740503adf4SYing-Chun Liu (PaulLiu) 
750503adf4SYing-Chun Liu (PaulLiu) 	/* wait for previous command finish */
760503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_waitcommand();
770503adf4SYing-Chun Liu (PaulLiu) 
780503adf4SYing-Chun Liu (PaulLiu) 	/* clean error status */
790503adf4SYing-Chun Liu (PaulLiu) 	status = mmio_read_32(reg_base + HC_HOSTSTATUS);
800503adf4SYing-Chun Liu (PaulLiu) 	if (status & HC_HSTST_MASK_ERROR_ALL)
810503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_HOSTSTATUS, status);
820503adf4SYing-Chun Liu (PaulLiu) 
830503adf4SYing-Chun Liu (PaulLiu) 	/* recording the command */
840503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.current_cmd = cmd & HC_CMD_COMMAND_MASK;
850503adf4SYing-Chun Liu (PaulLiu) 
860503adf4SYing-Chun Liu (PaulLiu) 	/* send the argument and command */
870503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_ARGUMENT, arg);
880503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_COMMAND, cmd | HC_CMD_ENABLE);
890503adf4SYing-Chun Liu (PaulLiu) }
900503adf4SYing-Chun Liu (PaulLiu) 
910503adf4SYing-Chun Liu (PaulLiu) /**
920503adf4SYing-Chun Liu (PaulLiu)  * Send the command and argument to the SDHOST, decorated with control
930503adf4SYing-Chun Liu (PaulLiu)  * flags.
940503adf4SYing-Chun Liu (PaulLiu)  *
950503adf4SYing-Chun Liu (PaulLiu)  * This function will use send_command_raw to send the commands to SDHOST.
960503adf4SYing-Chun Liu (PaulLiu)  * But before sending it will decorate the command with control flags specific
970503adf4SYing-Chun Liu (PaulLiu)  * to SDHOST.
980503adf4SYing-Chun Liu (PaulLiu)  */
990503adf4SYing-Chun Liu (PaulLiu) static void send_command_decorated(unsigned int cmd, unsigned int arg)
1000503adf4SYing-Chun Liu (PaulLiu) {
1010503adf4SYing-Chun Liu (PaulLiu) 	unsigned int cmd_flags = 0;
1020503adf4SYing-Chun Liu (PaulLiu) 
1030503adf4SYing-Chun Liu (PaulLiu) 	switch (cmd & HC_CMD_COMMAND_MASK) {
1040503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(0):
1050503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_RESPONSE_NONE;
1060503adf4SYing-Chun Liu (PaulLiu) 		break;
1070503adf4SYing-Chun Liu (PaulLiu) 	case MMC_ACMD(51):
1080503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_READ;
1090503adf4SYing-Chun Liu (PaulLiu) 		break;
1100503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(8):
1110503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(11):
1120503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(17):
1130503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(18):
1140503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_READ;
1150503adf4SYing-Chun Liu (PaulLiu) 		break;
1160503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(20):
1170503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(24):
1180503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(25):
1190503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_WRITE;
1200503adf4SYing-Chun Liu (PaulLiu) 		break;
1210503adf4SYing-Chun Liu (PaulLiu) 	case MMC_CMD(12):
1220503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_BUSY;
1230503adf4SYing-Chun Liu (PaulLiu) 		break;
1240503adf4SYing-Chun Liu (PaulLiu) 	default:
1250503adf4SYing-Chun Liu (PaulLiu) 		break;
1260503adf4SYing-Chun Liu (PaulLiu) 	}
1270503adf4SYing-Chun Liu (PaulLiu) 	send_command_raw(cmd | cmd_flags, arg);
1280503adf4SYing-Chun Liu (PaulLiu) }
1290503adf4SYing-Chun Liu (PaulLiu) 
1300503adf4SYing-Chun Liu (PaulLiu) /**
1310503adf4SYing-Chun Liu (PaulLiu)  * drains the FIFO on DATA port
1320503adf4SYing-Chun Liu (PaulLiu)  *
1330503adf4SYing-Chun Liu (PaulLiu)  * This function drains any data left in the DATA port.
1340503adf4SYing-Chun Liu (PaulLiu)  */
1350503adf4SYing-Chun Liu (PaulLiu) static void rpi3_drain_fifo(void)
1360503adf4SYing-Chun Liu (PaulLiu) {
1370503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
1380503adf4SYing-Chun Liu (PaulLiu) 	volatile int timeout = 100000;
1390503adf4SYing-Chun Liu (PaulLiu) 
1400503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_waitcommand();
1410503adf4SYing-Chun Liu (PaulLiu) 
1420503adf4SYing-Chun Liu (PaulLiu) 	while (mmio_read_32(reg_base + HC_HOSTSTATUS) & HC_HSTST_HAVEDATA) {
1430503adf4SYing-Chun Liu (PaulLiu) 		mmio_read_32(reg_base + HC_DATAPORT);
1440503adf4SYing-Chun Liu (PaulLiu) 		udelay(100);
1450503adf4SYing-Chun Liu (PaulLiu) 	}
1460503adf4SYing-Chun Liu (PaulLiu) 
1470503adf4SYing-Chun Liu (PaulLiu) 	while (1) {
1480503adf4SYing-Chun Liu (PaulLiu) 		uint32_t edm, fsm;
1490503adf4SYing-Chun Liu (PaulLiu) 
1500503adf4SYing-Chun Liu (PaulLiu) 		edm = mmio_read_32(reg_base + HC_DEBUG);
1510503adf4SYing-Chun Liu (PaulLiu) 		fsm = edm & HC_DBG_FSM_MASK;
1520503adf4SYing-Chun Liu (PaulLiu) 
1530503adf4SYing-Chun Liu (PaulLiu) 		if ((fsm == HC_DBG_FSM_IDENTMODE) ||
1540503adf4SYing-Chun Liu (PaulLiu) 		    (fsm == HC_DBG_FSM_DATAMODE))
1550503adf4SYing-Chun Liu (PaulLiu) 			break;
1560503adf4SYing-Chun Liu (PaulLiu) 
1570503adf4SYing-Chun Liu (PaulLiu) 		if ((fsm == HC_DBG_FSM_READWAIT) ||
1580503adf4SYing-Chun Liu (PaulLiu) 		    (fsm == HC_DBG_FSM_WRITESTART1) ||
1590503adf4SYing-Chun Liu (PaulLiu) 		    (fsm == HC_DBG_FSM_READDATA)) {
1600503adf4SYing-Chun Liu (PaulLiu) 			mmio_write_32(reg_base + HC_DEBUG,
1610503adf4SYing-Chun Liu (PaulLiu) 				      edm | HC_DBG_FORCE_DATA_MODE);
1620503adf4SYing-Chun Liu (PaulLiu) 			break;
1630503adf4SYing-Chun Liu (PaulLiu) 		}
1640503adf4SYing-Chun Liu (PaulLiu) 
1650503adf4SYing-Chun Liu (PaulLiu) 		if (--timeout <= 0) {
1660503adf4SYing-Chun Liu (PaulLiu) 			ERROR("rpi3_sdhost: %s cannot recover stat\n",
1670503adf4SYing-Chun Liu (PaulLiu) 			      __func__);
1680503adf4SYing-Chun Liu (PaulLiu) 			return;
1690503adf4SYing-Chun Liu (PaulLiu) 		}
1700503adf4SYing-Chun Liu (PaulLiu) 	}
1710503adf4SYing-Chun Liu (PaulLiu) }
1720503adf4SYing-Chun Liu (PaulLiu) 
1730503adf4SYing-Chun Liu (PaulLiu) /**
1740503adf4SYing-Chun Liu (PaulLiu)  * Dump SDHOST registers
1750503adf4SYing-Chun Liu (PaulLiu)  */
1760503adf4SYing-Chun Liu (PaulLiu) static void rpi3_sdhost_print_regs(void)
1770503adf4SYing-Chun Liu (PaulLiu) {
1780503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
1790503adf4SYing-Chun Liu (PaulLiu) 
1800503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_COMMAND:        0x%08x\n",
1810503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_COMMAND));
1820503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_ARGUMENT:       0x%08x\n",
1830503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_ARGUMENT));
1840503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_TIMEOUTCOUNTER: 0x%08x\n",
1850503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_TIMEOUTCOUNTER));
1860503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_CLOCKDIVISOR:   0x%08x\n",
1870503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_CLOCKDIVISOR));
1880503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_RESPONSE_0:     0x%08x\n",
1890503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_RESPONSE_0));
1900503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_RESPONSE_1:     0x%08x\n",
1910503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_RESPONSE_1));
1920503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_RESPONSE_2:     0x%08x\n",
1930503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_RESPONSE_2));
1940503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_RESPONSE_3:     0x%08x\n",
1950503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_RESPONSE_3));
1960503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_HOSTSTATUS:     0x%08x\n",
1970503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_HOSTSTATUS));
1980503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_POWER:          0x%08x\n",
1990503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_POWER));
2000503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_DEBUG:          0x%08x\n",
2010503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_DEBUG));
2020503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_HOSTCONFIG:     0x%08x\n",
2030503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_HOSTCONFIG));
2040503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_BLOCKSIZE:      0x%08x\n",
2050503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_BLOCKSIZE));
2060503adf4SYing-Chun Liu (PaulLiu) 	INFO("rpi3_sdhost: HC_BLOCKCOUNT:     0x%08x\n",
2070503adf4SYing-Chun Liu (PaulLiu) 	     mmio_read_32(reg_base + HC_BLOCKCOUNT));
2080503adf4SYing-Chun Liu (PaulLiu) }
2090503adf4SYing-Chun Liu (PaulLiu) 
2100503adf4SYing-Chun Liu (PaulLiu) /**
2110503adf4SYing-Chun Liu (PaulLiu)  * Reset SDHOST
2120503adf4SYing-Chun Liu (PaulLiu)  */
2130503adf4SYing-Chun Liu (PaulLiu) static void rpi3_sdhost_reset(void)
2140503adf4SYing-Chun Liu (PaulLiu) {
2150503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
2160503adf4SYing-Chun Liu (PaulLiu) 	unsigned int dbg;
2170503adf4SYing-Chun Liu (PaulLiu) 	uint32_t tmp1;
2180503adf4SYing-Chun Liu (PaulLiu) 
2190503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_POWER, 0);
2200503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_COMMAND, 0);
2210503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_ARGUMENT, 0);
2220503adf4SYing-Chun Liu (PaulLiu) 
2230503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_DEFAULT);
2240503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, 0);
2250503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_RESET);
2260503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
2270503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKSIZE, 0);
2280503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
2290503adf4SYing-Chun Liu (PaulLiu) 
2300503adf4SYing-Chun Liu (PaulLiu) 	dbg = mmio_read_32(reg_base + HC_DEBUG);
2310503adf4SYing-Chun Liu (PaulLiu) 	dbg &= ~((HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_READ_SHIFT) |
2320503adf4SYing-Chun Liu (PaulLiu) 		 (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_WRITE_SHIFT));
2330503adf4SYing-Chun Liu (PaulLiu) 	dbg |= (HC_FIFO_THRESH_READ << HC_DBG_FIFO_THRESH_READ_SHIFT) |
2340503adf4SYing-Chun Liu (PaulLiu) 		(HC_FIFO_THRESH_WRITE << HC_DBG_FIFO_THRESH_WRITE_SHIFT);
2350503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_DEBUG, dbg);
2360503adf4SYing-Chun Liu (PaulLiu) 	mdelay(250);
2370503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_POWER, 1);
2380503adf4SYing-Chun Liu (PaulLiu) 	mdelay(250);
2390503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.clk_rate = 0;
2400503adf4SYing-Chun Liu (PaulLiu) 
2410503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_MAXVAL);
2420503adf4SYing-Chun Liu (PaulLiu) 	tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
2430503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 | HC_HSTCF_INT_BUSY);
2440503adf4SYing-Chun Liu (PaulLiu) }
2450503adf4SYing-Chun Liu (PaulLiu) 
2460503adf4SYing-Chun Liu (PaulLiu) static void rpi3_sdhost_initialize(void)
2470503adf4SYing-Chun Liu (PaulLiu) {
2480503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
2490503adf4SYing-Chun Liu (PaulLiu) 
2500503adf4SYing-Chun Liu (PaulLiu) 	assert((rpi3_sdhost_params.reg_base & MMC_BLOCK_MASK) == 0);
2510503adf4SYing-Chun Liu (PaulLiu) 
2520503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_reset();
2530503adf4SYing-Chun Liu (PaulLiu) 
2540503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_PREFERVAL);
2550503adf4SYing-Chun Liu (PaulLiu) 	udelay(300);
2560503adf4SYing-Chun Liu (PaulLiu) }
2570503adf4SYing-Chun Liu (PaulLiu) 
2580503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd)
2590503adf4SYing-Chun Liu (PaulLiu) {
2600503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
2610503adf4SYing-Chun Liu (PaulLiu) 	int err = 0;
2620503adf4SYing-Chun Liu (PaulLiu) 	uint32_t cmd_idx;
2630503adf4SYing-Chun Liu (PaulLiu) 	uint32_t cmd_arg;
2640503adf4SYing-Chun Liu (PaulLiu) 	uint32_t cmd_flags = 0;
2650503adf4SYing-Chun Liu (PaulLiu) 	uint32_t intmask;
2660503adf4SYing-Chun Liu (PaulLiu) 
2670503adf4SYing-Chun Liu (PaulLiu) 	/* Wait for the command done */
2680503adf4SYing-Chun Liu (PaulLiu) 	err = rpi3_sdhost_waitcommand();
2690503adf4SYing-Chun Liu (PaulLiu) 	if (err != 0) {
2700503adf4SYing-Chun Liu (PaulLiu) 		WARN("previous command not done yet\n");
2710503adf4SYing-Chun Liu (PaulLiu) 		return err;
2720503adf4SYing-Chun Liu (PaulLiu) 	}
2730503adf4SYing-Chun Liu (PaulLiu) 
2740503adf4SYing-Chun Liu (PaulLiu) 	cmd_idx = cmd->cmd_idx & HC_CMD_COMMAND_MASK;
2750503adf4SYing-Chun Liu (PaulLiu) 
2760503adf4SYing-Chun Liu (PaulLiu) 	cmd_arg = cmd->cmd_arg;
2770503adf4SYing-Chun Liu (PaulLiu) 	if (cmd_idx == MMC_ACMD(51)) {
2780503adf4SYing-Chun Liu (PaulLiu) 		/* if previous cmd send to SDHOST is not MMC_CMD(55).
2790503adf4SYing-Chun Liu (PaulLiu) 		 * It means this MMC_ACMD(51) is a resend.
2800503adf4SYing-Chun Liu (PaulLiu) 		 * And we must also resend MMC_CMD(55) in this case
2810503adf4SYing-Chun Liu (PaulLiu) 		 */
2820503adf4SYing-Chun Liu (PaulLiu) 		if (rpi3_sdhost_params.current_cmd != MMC_CMD(55)) {
2830503adf4SYing-Chun Liu (PaulLiu) 			send_command_decorated(
2840503adf4SYing-Chun Liu (PaulLiu) 				MMC_CMD(55),
2850503adf4SYing-Chun Liu (PaulLiu) 				rpi3_sdhost_params.sdcard_rca <<
2860503adf4SYing-Chun Liu (PaulLiu) 				RCA_SHIFT_OFFSET);
2870503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_params.mmc_app_cmd = 1;
2880503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_waitcommand();
2890503adf4SYing-Chun Liu (PaulLiu) 
2900503adf4SYing-Chun Liu (PaulLiu) 			/* Also we need to call prepare to clean the buffer */
2910503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_prepare(0, (uintptr_t)NULL, 8);
2920503adf4SYing-Chun Liu (PaulLiu) 		}
2930503adf4SYing-Chun Liu (PaulLiu) 	}
2940503adf4SYing-Chun Liu (PaulLiu) 
2950503adf4SYing-Chun Liu (PaulLiu) 	/* We ignore MMC_CMD(12) sending from the TF-A's MMC driver
2960503adf4SYing-Chun Liu (PaulLiu) 	 * because we send MMC_CMD(12) by ourselves.
2970503adf4SYing-Chun Liu (PaulLiu) 	 */
2980503adf4SYing-Chun Liu (PaulLiu) 	if (cmd_idx == MMC_CMD(12))
2990503adf4SYing-Chun Liu (PaulLiu) 		return 0;
3000503adf4SYing-Chun Liu (PaulLiu) 
3010503adf4SYing-Chun Liu (PaulLiu) 	if ((cmd->resp_type & MMC_RSP_136) &&
3020503adf4SYing-Chun Liu (PaulLiu) 	    (cmd->resp_type & MMC_RSP_BUSY)) {
3030503adf4SYing-Chun Liu (PaulLiu) 		ERROR("rpi3_sdhost: unsupported response type!\n");
3040503adf4SYing-Chun Liu (PaulLiu) 		return -(EOPNOTSUPP);
3050503adf4SYing-Chun Liu (PaulLiu) 	}
3060503adf4SYing-Chun Liu (PaulLiu) 
3070503adf4SYing-Chun Liu (PaulLiu) 	if (cmd->resp_type & MMC_RSP_48 && cmd->resp_type != MMC_RESPONSE_R2) {
3080503adf4SYing-Chun Liu (PaulLiu) 		/* 48-bit command
3090503adf4SYing-Chun Liu (PaulLiu) 		 * We don't need to set any flags here because it is default.
3100503adf4SYing-Chun Liu (PaulLiu) 		 */
3110503adf4SYing-Chun Liu (PaulLiu) 	} else if (cmd->resp_type & MMC_RSP_136) {
3120503adf4SYing-Chun Liu (PaulLiu) 		/* 136-bit command */
3130503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_RESPONSE_LONG;
3140503adf4SYing-Chun Liu (PaulLiu) 	} else {
3150503adf4SYing-Chun Liu (PaulLiu) 		/* no respond command */
3160503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_RESPONSE_NONE;
3170503adf4SYing-Chun Liu (PaulLiu) 	}
3180503adf4SYing-Chun Liu (PaulLiu) 
3190503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.cmdbusy = 0;
3200503adf4SYing-Chun Liu (PaulLiu) 	if (cmd->resp_type & MMC_RSP_BUSY) {
3210503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_BUSY;
3220503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.cmdbusy = 1;
3230503adf4SYing-Chun Liu (PaulLiu) 	}
3240503adf4SYing-Chun Liu (PaulLiu) 
3250503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.mmc_app_cmd) {
3260503adf4SYing-Chun Liu (PaulLiu) 		switch (cmd_idx) {
3270503adf4SYing-Chun Liu (PaulLiu) 		case MMC_ACMD(41):
3280503adf4SYing-Chun Liu (PaulLiu) 			if (cmd_arg == OCR_HCS)
3290503adf4SYing-Chun Liu (PaulLiu) 				cmd_arg |= OCR_3_3_3_4;
3300503adf4SYing-Chun Liu (PaulLiu) 			break;
3310503adf4SYing-Chun Liu (PaulLiu) 		default:
3320503adf4SYing-Chun Liu (PaulLiu) 			break;
3330503adf4SYing-Chun Liu (PaulLiu) 		}
3340503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.mmc_app_cmd = 0;
3350503adf4SYing-Chun Liu (PaulLiu) 	}
3360503adf4SYing-Chun Liu (PaulLiu) 
3370503adf4SYing-Chun Liu (PaulLiu) 	if (cmd_idx == MMC_CMD(55))
3380503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.mmc_app_cmd = 1;
3390503adf4SYing-Chun Liu (PaulLiu) 
3400503adf4SYing-Chun Liu (PaulLiu) 	send_command_decorated(cmd_idx | cmd_flags, cmd_arg);
3410503adf4SYing-Chun Liu (PaulLiu) 
3420503adf4SYing-Chun Liu (PaulLiu) 	intmask = mmio_read_32(reg_base + HC_HOSTSTATUS);
3430503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.cmdbusy && (intmask & HC_HSTST_INT_BUSY)) {
3440503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_INT_BUSY);
3450503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.cmdbusy = 0;
3460503adf4SYing-Chun Liu (PaulLiu) 	}
3470503adf4SYing-Chun Liu (PaulLiu) 
3480503adf4SYing-Chun Liu (PaulLiu) 	if (!(cmd_flags & HC_CMD_RESPONSE_NONE)) {
3490503adf4SYing-Chun Liu (PaulLiu) 		err = rpi3_sdhost_waitcommand();
3500503adf4SYing-Chun Liu (PaulLiu) 		if (err != 0)
3510503adf4SYing-Chun Liu (PaulLiu) 			ERROR("rpi3_sdhost: cmd cannot be finished\n");
3520503adf4SYing-Chun Liu (PaulLiu) 	}
3530503adf4SYing-Chun Liu (PaulLiu) 
3540503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[0] = mmio_read_32(reg_base + HC_RESPONSE_0);
3550503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[1] = mmio_read_32(reg_base + HC_RESPONSE_1);
3560503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[2] = mmio_read_32(reg_base + HC_RESPONSE_2);
3570503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[3] = mmio_read_32(reg_base + HC_RESPONSE_3);
3580503adf4SYing-Chun Liu (PaulLiu) 
3590503adf4SYing-Chun Liu (PaulLiu) 	if (mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_FAILED) {
3600503adf4SYing-Chun Liu (PaulLiu) 		uint32_t sdhsts = mmio_read_32(reg_base + HC_HOSTSTATUS);
3610503adf4SYing-Chun Liu (PaulLiu) 
3620503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_HOSTSTATUS,
3630503adf4SYing-Chun Liu (PaulLiu) 			      HC_HSTST_MASK_ERROR_ALL);
3640503adf4SYing-Chun Liu (PaulLiu) 
365*2c8ef2aeSYing-Chun Liu (PaulLiu) 		/*
366*2c8ef2aeSYing-Chun Liu (PaulLiu) 		 * If the command SEND_OP_COND returns with CRC7 error,
367*2c8ef2aeSYing-Chun Liu (PaulLiu) 		 * it can be considered as having completed successfully.
368*2c8ef2aeSYing-Chun Liu (PaulLiu) 		 */
3690503adf4SYing-Chun Liu (PaulLiu) 		if (!(sdhsts & HC_HSTST_ERROR_CRC7)
370*2c8ef2aeSYing-Chun Liu (PaulLiu) 		    || (cmd_idx != MMC_CMD(1))) {
3710503adf4SYing-Chun Liu (PaulLiu) 			if (sdhsts & HC_HSTST_TIMEOUT_CMD) {
3720503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost: timeout status 0x%x\n",
3730503adf4SYing-Chun Liu (PaulLiu) 				      sdhsts);
3740503adf4SYing-Chun Liu (PaulLiu) 				err = -(ETIMEDOUT);
3750503adf4SYing-Chun Liu (PaulLiu) 			} else {
3760503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost: unknown err, cmd = 0x%x\n",
3770503adf4SYing-Chun Liu (PaulLiu) 				      mmio_read_32(reg_base + HC_COMMAND));
3780503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost status: 0x%x\n", sdhsts);
3790503adf4SYing-Chun Liu (PaulLiu) 				err = -(EILSEQ);
3800503adf4SYing-Chun Liu (PaulLiu) 			}
3810503adf4SYing-Chun Liu (PaulLiu) 		}
3820503adf4SYing-Chun Liu (PaulLiu) 	}
3830503adf4SYing-Chun Liu (PaulLiu) 
3840503adf4SYing-Chun Liu (PaulLiu) 	if ((!err) && (cmd_idx == MMC_CMD(3))) {
3850503adf4SYing-Chun Liu (PaulLiu) 		/* we keep the RCA in case to send MMC_CMD(55) ourselves */
3860503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.sdcard_rca = (cmd->resp_data[0]
3870503adf4SYing-Chun Liu (PaulLiu) 						 & 0xFFFF0000U) >> 16;
3880503adf4SYing-Chun Liu (PaulLiu) 	}
3890503adf4SYing-Chun Liu (PaulLiu) 
3900503adf4SYing-Chun Liu (PaulLiu) 	return err;
3910503adf4SYing-Chun Liu (PaulLiu) }
3920503adf4SYing-Chun Liu (PaulLiu) 
3930503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_set_clock(unsigned int clk)
3940503adf4SYing-Chun Liu (PaulLiu) {
3950503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
3960503adf4SYing-Chun Liu (PaulLiu) 	uint32_t max_clk = 250000000;
3970503adf4SYing-Chun Liu (PaulLiu) 	uint32_t div;
3980503adf4SYing-Chun Liu (PaulLiu) 
3990503adf4SYing-Chun Liu (PaulLiu) 	if (clk < 100000) {
4000503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_CLOCKDIVISOR,
4010503adf4SYing-Chun Liu (PaulLiu) 			      HC_CLOCKDIVISOR_MAXVAL);
4020503adf4SYing-Chun Liu (PaulLiu) 		return 0;
4030503adf4SYing-Chun Liu (PaulLiu) 	}
4040503adf4SYing-Chun Liu (PaulLiu) 
4050503adf4SYing-Chun Liu (PaulLiu) 	div = max_clk / clk;
4060503adf4SYing-Chun Liu (PaulLiu) 	if (div < 2)
4070503adf4SYing-Chun Liu (PaulLiu) 		div = 2;
4080503adf4SYing-Chun Liu (PaulLiu) 
4090503adf4SYing-Chun Liu (PaulLiu) 	if ((max_clk / div) > clk)
4100503adf4SYing-Chun Liu (PaulLiu) 		div++;
4110503adf4SYing-Chun Liu (PaulLiu) 
4120503adf4SYing-Chun Liu (PaulLiu) 	div -= 2;
4130503adf4SYing-Chun Liu (PaulLiu) 	if (div > HC_CLOCKDIVISOR_MAXVAL)
4140503adf4SYing-Chun Liu (PaulLiu) 		div = HC_CLOCKDIVISOR_MAXVAL;
4150503adf4SYing-Chun Liu (PaulLiu) 
4160503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.clk_rate = max_clk / (div + 2);
4170503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.ns_per_fifo_word = (1000000000 /
4180503adf4SYing-Chun Liu (PaulLiu) 					       rpi3_sdhost_params.clk_rate)
4190503adf4SYing-Chun Liu (PaulLiu) 		* 8;
4200503adf4SYing-Chun Liu (PaulLiu) 
4210503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, div);
4220503adf4SYing-Chun Liu (PaulLiu) 	return 0;
4230503adf4SYing-Chun Liu (PaulLiu) }
4240503adf4SYing-Chun Liu (PaulLiu) 
4250503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width)
4260503adf4SYing-Chun Liu (PaulLiu) {
4270503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
4280503adf4SYing-Chun Liu (PaulLiu) 	uint32_t tmp1;
4290503adf4SYing-Chun Liu (PaulLiu) 
4300503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_set_clock(clk);
4310503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Changing clock to %dHz for data mode\n", clk);
4320503adf4SYing-Chun Liu (PaulLiu) 
4330503adf4SYing-Chun Liu (PaulLiu) 	if (width != MMC_BUS_WIDTH_4 && width != MMC_BUS_WIDTH_1) {
4340503adf4SYing-Chun Liu (PaulLiu) 		ERROR("rpi3_sdhost: width %d not supported\n", width);
4350503adf4SYing-Chun Liu (PaulLiu) 		return -(EOPNOTSUPP);
4360503adf4SYing-Chun Liu (PaulLiu) 	}
4370503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.bus_width = width;
4380503adf4SYing-Chun Liu (PaulLiu) 
4390503adf4SYing-Chun Liu (PaulLiu) 	tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
4400503adf4SYing-Chun Liu (PaulLiu) 	tmp1 &= ~(HC_HSTCF_EXTBUS_4BIT);
4410503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.bus_width == MMC_BUS_WIDTH_4)
4420503adf4SYing-Chun Liu (PaulLiu) 		tmp1 |= HC_HSTCF_EXTBUS_4BIT;
4430503adf4SYing-Chun Liu (PaulLiu) 
4440503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1);
4450503adf4SYing-Chun Liu (PaulLiu) 	tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
4460503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 |
4470503adf4SYing-Chun Liu (PaulLiu) 		      HC_HSTCF_SLOW_CARD | HC_HSTCF_INTBUS_WIDE);
4480503adf4SYing-Chun Liu (PaulLiu) 
4490503adf4SYing-Chun Liu (PaulLiu) 	return 0;
4500503adf4SYing-Chun Liu (PaulLiu) }
4510503adf4SYing-Chun Liu (PaulLiu) 
4520503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size)
4530503adf4SYing-Chun Liu (PaulLiu) {
4540503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
4550503adf4SYing-Chun Liu (PaulLiu) 	size_t blocks;
4560503adf4SYing-Chun Liu (PaulLiu) 	size_t blocksize;
4570503adf4SYing-Chun Liu (PaulLiu) 
4580503adf4SYing-Chun Liu (PaulLiu) 	if (size < 512) {
4590503adf4SYing-Chun Liu (PaulLiu) 		blocksize = size;
4600503adf4SYing-Chun Liu (PaulLiu) 		blocks = 1;
4610503adf4SYing-Chun Liu (PaulLiu) 	} else {
4620503adf4SYing-Chun Liu (PaulLiu) 		blocksize = 512;
4630503adf4SYing-Chun Liu (PaulLiu) 		blocks = size / blocksize;
4640503adf4SYing-Chun Liu (PaulLiu) 		if (size % blocksize != 0)
4650503adf4SYing-Chun Liu (PaulLiu) 			blocks++;
4660503adf4SYing-Chun Liu (PaulLiu) 	}
4670503adf4SYing-Chun Liu (PaulLiu) 
4680503adf4SYing-Chun Liu (PaulLiu) 	rpi3_drain_fifo();
4690503adf4SYing-Chun Liu (PaulLiu) 
4700503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKSIZE, blocksize);
4710503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKCOUNT, blocks);
4720503adf4SYing-Chun Liu (PaulLiu) 	udelay(100);
4730503adf4SYing-Chun Liu (PaulLiu) 	return 0;
4740503adf4SYing-Chun Liu (PaulLiu) }
4750503adf4SYing-Chun Liu (PaulLiu) 
4760503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size)
4770503adf4SYing-Chun Liu (PaulLiu) {
4780503adf4SYing-Chun Liu (PaulLiu) 	int err = 0;
4790503adf4SYing-Chun Liu (PaulLiu) 	uint32_t *buf1 = ((uint32_t *) buf);
4800503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
4810503adf4SYing-Chun Liu (PaulLiu) 	int timeout = 100000;
4820503adf4SYing-Chun Liu (PaulLiu) 	int remaining_words = 0;
4830503adf4SYing-Chun Liu (PaulLiu) 
4840503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 0; i < size / 4; i++) {
4850503adf4SYing-Chun Liu (PaulLiu) 		volatile int t = timeout;
4860503adf4SYing-Chun Liu (PaulLiu) 		uint32_t hsts_err;
4870503adf4SYing-Chun Liu (PaulLiu) 
4880503adf4SYing-Chun Liu (PaulLiu) 		while ((mmio_read_32(reg_base + HC_HOSTSTATUS)
4890503adf4SYing-Chun Liu (PaulLiu) 			& HC_HSTST_HAVEDATA) == 0) {
4900503adf4SYing-Chun Liu (PaulLiu) 			if (t == 0) {
4910503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost: fifo timeout after %dus\n",
4920503adf4SYing-Chun Liu (PaulLiu) 				      timeout);
4930503adf4SYing-Chun Liu (PaulLiu) 				err = -(ETIMEDOUT);
4940503adf4SYing-Chun Liu (PaulLiu) 				break;
4950503adf4SYing-Chun Liu (PaulLiu) 			}
4960503adf4SYing-Chun Liu (PaulLiu) 			t--;
4970503adf4SYing-Chun Liu (PaulLiu) 			udelay(10);
4980503adf4SYing-Chun Liu (PaulLiu) 		}
4990503adf4SYing-Chun Liu (PaulLiu) 		if (t == 0)
5000503adf4SYing-Chun Liu (PaulLiu) 			break;
5010503adf4SYing-Chun Liu (PaulLiu) 
5020503adf4SYing-Chun Liu (PaulLiu) 		uint32_t data = mmio_read_32(reg_base + HC_DATAPORT);
5030503adf4SYing-Chun Liu (PaulLiu) 
5040503adf4SYing-Chun Liu (PaulLiu) 		hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
5050503adf4SYing-Chun Liu (PaulLiu) 			& HC_HSTST_MASK_ERROR_ALL;
5060503adf4SYing-Chun Liu (PaulLiu) 		if (hsts_err) {
5070503adf4SYing-Chun Liu (PaulLiu) 			ERROR("rpi3_sdhost: transfer FIFO word %d: 0x%x\n",
5080503adf4SYing-Chun Liu (PaulLiu) 			      i,
5090503adf4SYing-Chun Liu (PaulLiu) 			      mmio_read_32(reg_base + HC_HOSTSTATUS));
5100503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_print_regs();
5110503adf4SYing-Chun Liu (PaulLiu) 
5120503adf4SYing-Chun Liu (PaulLiu) 			err = -(EILSEQ);
5130503adf4SYing-Chun Liu (PaulLiu) 
5140503adf4SYing-Chun Liu (PaulLiu) 			/* clean the error status */
5150503adf4SYing-Chun Liu (PaulLiu) 			mmio_write_32(reg_base + HC_HOSTSTATUS, hsts_err);
5160503adf4SYing-Chun Liu (PaulLiu) 		}
5170503adf4SYing-Chun Liu (PaulLiu) 
5180503adf4SYing-Chun Liu (PaulLiu) 		if (buf1)
5190503adf4SYing-Chun Liu (PaulLiu) 			buf1[i] = data;
5200503adf4SYing-Chun Liu (PaulLiu) 
5210503adf4SYing-Chun Liu (PaulLiu) 		/* speeding up if the remaining words are still a lot */
5220503adf4SYing-Chun Liu (PaulLiu) 		remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
5230503adf4SYing-Chun Liu (PaulLiu) 			& HC_DBG_FIFO_THRESH_MASK;
5240503adf4SYing-Chun Liu (PaulLiu) 		if (remaining_words >= 7)
5250503adf4SYing-Chun Liu (PaulLiu) 			continue;
5260503adf4SYing-Chun Liu (PaulLiu) 
5270503adf4SYing-Chun Liu (PaulLiu) 		/* delay. slowing down the read process */
5280503adf4SYing-Chun Liu (PaulLiu) 		udelay(100);
5290503adf4SYing-Chun Liu (PaulLiu) 	}
5300503adf4SYing-Chun Liu (PaulLiu) 
5310503adf4SYing-Chun Liu (PaulLiu) 	/* We decide to stop by ourselves.
5320503adf4SYing-Chun Liu (PaulLiu) 	 * It is because MMC_CMD(18) -> MMC_CMD(13) -> MMC_CMD(12)
5330503adf4SYing-Chun Liu (PaulLiu) 	 * doesn't work for RPi3 SDHost.
5340503adf4SYing-Chun Liu (PaulLiu) 	 */
5350503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.current_cmd == MMC_CMD(18))
5360503adf4SYing-Chun Liu (PaulLiu) 		send_command_decorated(MMC_CMD(12), 0);
5370503adf4SYing-Chun Liu (PaulLiu) 
5380503adf4SYing-Chun Liu (PaulLiu) 	return err;
5390503adf4SYing-Chun Liu (PaulLiu) }
5400503adf4SYing-Chun Liu (PaulLiu) 
5410503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size)
5420503adf4SYing-Chun Liu (PaulLiu) {
5430503adf4SYing-Chun Liu (PaulLiu) 	uint32_t *buf1 = ((uint32_t *) buf);
5440503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
5450503adf4SYing-Chun Liu (PaulLiu) 	int err = 0;
5460503adf4SYing-Chun Liu (PaulLiu) 	int remaining_words = 0;
5470503adf4SYing-Chun Liu (PaulLiu) 
5480503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 0; i < size / 4; i++) {
5490503adf4SYing-Chun Liu (PaulLiu) 		uint32_t hsts_err;
5500503adf4SYing-Chun Liu (PaulLiu) 		uint32_t data = buf1[i];
5510503adf4SYing-Chun Liu (PaulLiu) 		uint32_t dbg;
5520503adf4SYing-Chun Liu (PaulLiu) 		uint32_t fsm_state;
5530503adf4SYing-Chun Liu (PaulLiu) 
5540503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_DATAPORT, data);
5550503adf4SYing-Chun Liu (PaulLiu) 
5560503adf4SYing-Chun Liu (PaulLiu) 		dbg = mmio_read_32(reg_base + HC_DEBUG);
5570503adf4SYing-Chun Liu (PaulLiu) 		fsm_state = dbg & HC_DBG_FSM_MASK;
5580503adf4SYing-Chun Liu (PaulLiu) 		if (fsm_state != HC_DBG_FSM_WRITEDATA
5590503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITESTART1
5600503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITESTART2
5610503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITECRC
5620503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITEWAIT1
5630503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITEWAIT2) {
5640503adf4SYing-Chun Liu (PaulLiu) 			hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
5650503adf4SYing-Chun Liu (PaulLiu) 				& HC_HSTST_MASK_ERROR_ALL;
5660503adf4SYing-Chun Liu (PaulLiu) 			if (hsts_err)
5670503adf4SYing-Chun Liu (PaulLiu) 				err = -(EILSEQ);
5680503adf4SYing-Chun Liu (PaulLiu) 		}
5690503adf4SYing-Chun Liu (PaulLiu) 
5700503adf4SYing-Chun Liu (PaulLiu) 		/* speeding up if the remaining words are not many */
5710503adf4SYing-Chun Liu (PaulLiu) 		remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
5720503adf4SYing-Chun Liu (PaulLiu) 			& HC_DBG_FIFO_THRESH_MASK;
5730503adf4SYing-Chun Liu (PaulLiu) 		if (remaining_words <= 4)
5740503adf4SYing-Chun Liu (PaulLiu) 			continue;
5750503adf4SYing-Chun Liu (PaulLiu) 
5760503adf4SYing-Chun Liu (PaulLiu) 		udelay(100);
5770503adf4SYing-Chun Liu (PaulLiu) 	}
5780503adf4SYing-Chun Liu (PaulLiu) 
5790503adf4SYing-Chun Liu (PaulLiu) 	/* We decide to stop by ourselves.
5800503adf4SYing-Chun Liu (PaulLiu) 	 * It is because MMC_CMD(25) -> MMC_CMD(13) -> MMC_CMD(12)
5810503adf4SYing-Chun Liu (PaulLiu) 	 * doesn't work for RPi3 SDHost.
5820503adf4SYing-Chun Liu (PaulLiu) 	 */
5830503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.current_cmd == MMC_CMD(25))
5840503adf4SYing-Chun Liu (PaulLiu) 		send_command_decorated(MMC_CMD(12), 0);
5850503adf4SYing-Chun Liu (PaulLiu) 
5860503adf4SYing-Chun Liu (PaulLiu) 	return err;
5870503adf4SYing-Chun Liu (PaulLiu) }
5880503adf4SYing-Chun Liu (PaulLiu) 
5890503adf4SYing-Chun Liu (PaulLiu) void rpi3_sdhost_init(struct rpi3_sdhost_params *params,
5900503adf4SYing-Chun Liu (PaulLiu) 		    struct mmc_device_info *mmc_dev_info)
5910503adf4SYing-Chun Liu (PaulLiu) {
5920503adf4SYing-Chun Liu (PaulLiu) 	assert((params != 0) &&
5930503adf4SYing-Chun Liu (PaulLiu) 	       ((params->reg_base & MMC_BLOCK_MASK) == 0));
5940503adf4SYing-Chun Liu (PaulLiu) 
5950503adf4SYing-Chun Liu (PaulLiu) 	memcpy(&rpi3_sdhost_params, params, sizeof(struct rpi3_sdhost_params));
5960503adf4SYing-Chun Liu (PaulLiu) 
5970503adf4SYing-Chun Liu (PaulLiu) 	/* backup GPIO 48 to 53 configurations */
5980503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++) {
5990503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.gpio48_pinselect[i - 48]
6000503adf4SYing-Chun Liu (PaulLiu) 			= rpi3_gpio_get_select(i);
6010503adf4SYing-Chun Liu (PaulLiu) 		VERBOSE("rpi3_sdhost: Original GPIO state %d: %d\n",
6020503adf4SYing-Chun Liu (PaulLiu) 			i,
6030503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_params.gpio48_pinselect[i - 48]);
6040503adf4SYing-Chun Liu (PaulLiu) 	}
6050503adf4SYing-Chun Liu (PaulLiu) 
6060503adf4SYing-Chun Liu (PaulLiu) 	/* setting pull resistors for 48 to 53.
607*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * It is debatable to set SD_CLK to UP or NONE. We massively
608*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * tested different brands of SD Cards and found NONE works
609*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * most stable.
610*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 *
611*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 48 (SD_CLK) to GPIO_PULL_NONE
612*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 49 (SD_CMD) to GPIO_PULL_UP
613*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 50 (SD_D0)  to GPIO_PULL_UP
614*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 51 (SD_D1)  to GPIO_PULL_UP
615*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 52 (SD_D2)  to GPIO_PULL_UP
616*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 53 (SD_D3)  to GPIO_PULL_UP
6170503adf4SYing-Chun Liu (PaulLiu) 	 */
618*2c8ef2aeSYing-Chun Liu (PaulLiu) 	gpio_set_pull(48, GPIO_PULL_NONE);
6190503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 49; i <= 53; i++)
620*2c8ef2aeSYing-Chun Liu (PaulLiu) 		gpio_set_pull(i, GPIO_PULL_UP);
6210503adf4SYing-Chun Liu (PaulLiu) 
6220503adf4SYing-Chun Liu (PaulLiu) 	/* Set pin 48-53 to alt-0. It means route SDHOST to card slot */
6230503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++)
6240503adf4SYing-Chun Liu (PaulLiu) 		rpi3_gpio_set_select(i, RPI3_GPIO_FUNC_ALT0);
6250503adf4SYing-Chun Liu (PaulLiu) 
6260503adf4SYing-Chun Liu (PaulLiu) 	mmc_init(&rpi3_sdhost_ops, params->clk_rate, params->bus_width,
6270503adf4SYing-Chun Liu (PaulLiu) 		 params->flags, mmc_dev_info);
6280503adf4SYing-Chun Liu (PaulLiu) }
6290503adf4SYing-Chun Liu (PaulLiu) 
6300503adf4SYing-Chun Liu (PaulLiu) void rpi3_sdhost_stop(void)
6310503adf4SYing-Chun Liu (PaulLiu) {
6320503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
6330503adf4SYing-Chun Liu (PaulLiu) 
6340503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Shutting down: drain FIFO out\n");
6350503adf4SYing-Chun Liu (PaulLiu) 	rpi3_drain_fifo();
6360503adf4SYing-Chun Liu (PaulLiu) 
6370503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Shutting down: slowing down the clock\n");
6380503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base+HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_SLOWVAL);
6390503adf4SYing-Chun Liu (PaulLiu) 	udelay(500);
6400503adf4SYing-Chun Liu (PaulLiu) 
6410503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Shutting down: put SDHost into idle state\n");
6420503adf4SYing-Chun Liu (PaulLiu) 	send_command_decorated(MMC_CMD(0), 0);
6430503adf4SYing-Chun Liu (PaulLiu) 	udelay(500);
6440503adf4SYing-Chun Liu (PaulLiu) 
6450503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_COMMAND, 0);
6460503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_ARGUMENT, 0);
6470503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_IDLE);
6480503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_STOPVAL);
6490503adf4SYing-Chun Liu (PaulLiu) 
6500503adf4SYing-Chun Liu (PaulLiu) 	udelay(100);
6510503adf4SYing-Chun Liu (PaulLiu) 
6520503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_POWER, 0);
6530503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
6540503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKSIZE, 0x400);
6550503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
6560503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTSTATUS, 0x7f8);
6570503adf4SYing-Chun Liu (PaulLiu) 
6580503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_COMMAND, 0);
6590503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_ARGUMENT, 0);
6600503adf4SYing-Chun Liu (PaulLiu) 
6610503adf4SYing-Chun Liu (PaulLiu) 	udelay(100);
6620503adf4SYing-Chun Liu (PaulLiu) 
6630503adf4SYing-Chun Liu (PaulLiu) 	/* Restore the pinmux to original state */
6640503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++) {
6650503adf4SYing-Chun Liu (PaulLiu) 		rpi3_gpio_set_select(i,
6660503adf4SYing-Chun Liu (PaulLiu) 				     rpi3_sdhost_params.gpio48_pinselect[i-48]);
6670503adf4SYing-Chun Liu (PaulLiu) 	}
6680503adf4SYing-Chun Liu (PaulLiu) 
669*2c8ef2aeSYing-Chun Liu (PaulLiu) 	/* Reset the pull resistors before entering BL33.
670*2c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 48 (SD_CLK) to GPIO_PULL_UP
6710503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 49 (SD_CMD) to GPIO_PULL_UP
6720503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 50 (SD_D0)  to GPIO_PULL_UP
6730503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 51 (SD_D1)  to GPIO_PULL_UP
6740503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 52 (SD_D2)  to GPIO_PULL_UP
6750503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 53 (SD_D3)  to GPIO_PULL_UP
6760503adf4SYing-Chun Liu (PaulLiu) 	 */
677*2c8ef2aeSYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++)
6780503adf4SYing-Chun Liu (PaulLiu) 		gpio_set_pull(i, GPIO_PULL_UP);
6790503adf4SYing-Chun Liu (PaulLiu) }
680