xref: /rk3399_ARM-atf/drivers/rpi3/sdhost/rpi3_sdhost.c (revision bd96d533dc28c4c938aa54905787688823cbccac)
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) 	assert((rpi3_sdhost_params.reg_base & MMC_BLOCK_MASK) == 0);
2490503adf4SYing-Chun Liu (PaulLiu) 
2500503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_reset();
2510503adf4SYing-Chun Liu (PaulLiu) 
252*bd96d533SRob Newberry 	rpi3_sdhost_set_ios(rpi3_sdhost_params.clk_rate_initial,
253*bd96d533SRob Newberry 		rpi3_sdhost_params.bus_width);
2540503adf4SYing-Chun Liu (PaulLiu) 	udelay(300);
2550503adf4SYing-Chun Liu (PaulLiu) }
2560503adf4SYing-Chun Liu (PaulLiu) 
2570503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd)
2580503adf4SYing-Chun Liu (PaulLiu) {
2590503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
2600503adf4SYing-Chun Liu (PaulLiu) 	int err = 0;
2610503adf4SYing-Chun Liu (PaulLiu) 	uint32_t cmd_idx;
2620503adf4SYing-Chun Liu (PaulLiu) 	uint32_t cmd_arg;
2630503adf4SYing-Chun Liu (PaulLiu) 	uint32_t cmd_flags = 0;
2640503adf4SYing-Chun Liu (PaulLiu) 	uint32_t intmask;
2650503adf4SYing-Chun Liu (PaulLiu) 
2660503adf4SYing-Chun Liu (PaulLiu) 	/* Wait for the command done */
2670503adf4SYing-Chun Liu (PaulLiu) 	err = rpi3_sdhost_waitcommand();
2680503adf4SYing-Chun Liu (PaulLiu) 	if (err != 0) {
2690503adf4SYing-Chun Liu (PaulLiu) 		WARN("previous command not done yet\n");
2700503adf4SYing-Chun Liu (PaulLiu) 		return err;
2710503adf4SYing-Chun Liu (PaulLiu) 	}
2720503adf4SYing-Chun Liu (PaulLiu) 
2730503adf4SYing-Chun Liu (PaulLiu) 	cmd_idx = cmd->cmd_idx & HC_CMD_COMMAND_MASK;
2740503adf4SYing-Chun Liu (PaulLiu) 
2750503adf4SYing-Chun Liu (PaulLiu) 	cmd_arg = cmd->cmd_arg;
2760503adf4SYing-Chun Liu (PaulLiu) 	if (cmd_idx == MMC_ACMD(51)) {
2770503adf4SYing-Chun Liu (PaulLiu) 		/* if previous cmd send to SDHOST is not MMC_CMD(55).
2780503adf4SYing-Chun Liu (PaulLiu) 		 * It means this MMC_ACMD(51) is a resend.
2790503adf4SYing-Chun Liu (PaulLiu) 		 * And we must also resend MMC_CMD(55) in this case
2800503adf4SYing-Chun Liu (PaulLiu) 		 */
2810503adf4SYing-Chun Liu (PaulLiu) 		if (rpi3_sdhost_params.current_cmd != MMC_CMD(55)) {
2820503adf4SYing-Chun Liu (PaulLiu) 			send_command_decorated(
2830503adf4SYing-Chun Liu (PaulLiu) 				MMC_CMD(55),
2840503adf4SYing-Chun Liu (PaulLiu) 				rpi3_sdhost_params.sdcard_rca <<
2850503adf4SYing-Chun Liu (PaulLiu) 				RCA_SHIFT_OFFSET);
2860503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_params.mmc_app_cmd = 1;
2870503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_waitcommand();
2880503adf4SYing-Chun Liu (PaulLiu) 
2890503adf4SYing-Chun Liu (PaulLiu) 			/* Also we need to call prepare to clean the buffer */
2900503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_prepare(0, (uintptr_t)NULL, 8);
2910503adf4SYing-Chun Liu (PaulLiu) 		}
2920503adf4SYing-Chun Liu (PaulLiu) 	}
2930503adf4SYing-Chun Liu (PaulLiu) 
2940503adf4SYing-Chun Liu (PaulLiu) 	/* We ignore MMC_CMD(12) sending from the TF-A's MMC driver
2950503adf4SYing-Chun Liu (PaulLiu) 	 * because we send MMC_CMD(12) by ourselves.
2960503adf4SYing-Chun Liu (PaulLiu) 	 */
2970503adf4SYing-Chun Liu (PaulLiu) 	if (cmd_idx == MMC_CMD(12))
2980503adf4SYing-Chun Liu (PaulLiu) 		return 0;
2990503adf4SYing-Chun Liu (PaulLiu) 
3000503adf4SYing-Chun Liu (PaulLiu) 	if ((cmd->resp_type & MMC_RSP_136) &&
3010503adf4SYing-Chun Liu (PaulLiu) 	    (cmd->resp_type & MMC_RSP_BUSY)) {
3020503adf4SYing-Chun Liu (PaulLiu) 		ERROR("rpi3_sdhost: unsupported response type!\n");
3030503adf4SYing-Chun Liu (PaulLiu) 		return -(EOPNOTSUPP);
3040503adf4SYing-Chun Liu (PaulLiu) 	}
3050503adf4SYing-Chun Liu (PaulLiu) 
3060503adf4SYing-Chun Liu (PaulLiu) 	if (cmd->resp_type & MMC_RSP_48 && cmd->resp_type != MMC_RESPONSE_R2) {
3070503adf4SYing-Chun Liu (PaulLiu) 		/* 48-bit command
3080503adf4SYing-Chun Liu (PaulLiu) 		 * We don't need to set any flags here because it is default.
3090503adf4SYing-Chun Liu (PaulLiu) 		 */
3100503adf4SYing-Chun Liu (PaulLiu) 	} else if (cmd->resp_type & MMC_RSP_136) {
3110503adf4SYing-Chun Liu (PaulLiu) 		/* 136-bit command */
3120503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_RESPONSE_LONG;
3130503adf4SYing-Chun Liu (PaulLiu) 	} else {
3140503adf4SYing-Chun Liu (PaulLiu) 		/* no respond command */
3150503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_RESPONSE_NONE;
3160503adf4SYing-Chun Liu (PaulLiu) 	}
3170503adf4SYing-Chun Liu (PaulLiu) 
3180503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.cmdbusy = 0;
3190503adf4SYing-Chun Liu (PaulLiu) 	if (cmd->resp_type & MMC_RSP_BUSY) {
3200503adf4SYing-Chun Liu (PaulLiu) 		cmd_flags |= HC_CMD_BUSY;
3210503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.cmdbusy = 1;
3220503adf4SYing-Chun Liu (PaulLiu) 	}
3230503adf4SYing-Chun Liu (PaulLiu) 
3240503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.mmc_app_cmd) {
3250503adf4SYing-Chun Liu (PaulLiu) 		switch (cmd_idx) {
3260503adf4SYing-Chun Liu (PaulLiu) 		case MMC_ACMD(41):
3270503adf4SYing-Chun Liu (PaulLiu) 			if (cmd_arg == OCR_HCS)
3280503adf4SYing-Chun Liu (PaulLiu) 				cmd_arg |= OCR_3_3_3_4;
3290503adf4SYing-Chun Liu (PaulLiu) 			break;
3300503adf4SYing-Chun Liu (PaulLiu) 		default:
3310503adf4SYing-Chun Liu (PaulLiu) 			break;
3320503adf4SYing-Chun Liu (PaulLiu) 		}
3330503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.mmc_app_cmd = 0;
3340503adf4SYing-Chun Liu (PaulLiu) 	}
3350503adf4SYing-Chun Liu (PaulLiu) 
3360503adf4SYing-Chun Liu (PaulLiu) 	if (cmd_idx == MMC_CMD(55))
3370503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.mmc_app_cmd = 1;
3380503adf4SYing-Chun Liu (PaulLiu) 
3390503adf4SYing-Chun Liu (PaulLiu) 	send_command_decorated(cmd_idx | cmd_flags, cmd_arg);
3400503adf4SYing-Chun Liu (PaulLiu) 
3410503adf4SYing-Chun Liu (PaulLiu) 	intmask = mmio_read_32(reg_base + HC_HOSTSTATUS);
3420503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.cmdbusy && (intmask & HC_HSTST_INT_BUSY)) {
3430503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_INT_BUSY);
3440503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.cmdbusy = 0;
3450503adf4SYing-Chun Liu (PaulLiu) 	}
3460503adf4SYing-Chun Liu (PaulLiu) 
3470503adf4SYing-Chun Liu (PaulLiu) 	if (!(cmd_flags & HC_CMD_RESPONSE_NONE)) {
3480503adf4SYing-Chun Liu (PaulLiu) 		err = rpi3_sdhost_waitcommand();
3490503adf4SYing-Chun Liu (PaulLiu) 		if (err != 0)
3500503adf4SYing-Chun Liu (PaulLiu) 			ERROR("rpi3_sdhost: cmd cannot be finished\n");
3510503adf4SYing-Chun Liu (PaulLiu) 	}
3520503adf4SYing-Chun Liu (PaulLiu) 
3530503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[0] = mmio_read_32(reg_base + HC_RESPONSE_0);
3540503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[1] = mmio_read_32(reg_base + HC_RESPONSE_1);
3550503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[2] = mmio_read_32(reg_base + HC_RESPONSE_2);
3560503adf4SYing-Chun Liu (PaulLiu) 	cmd->resp_data[3] = mmio_read_32(reg_base + HC_RESPONSE_3);
3570503adf4SYing-Chun Liu (PaulLiu) 
3580503adf4SYing-Chun Liu (PaulLiu) 	if (mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_FAILED) {
3590503adf4SYing-Chun Liu (PaulLiu) 		uint32_t sdhsts = mmio_read_32(reg_base + HC_HOSTSTATUS);
3600503adf4SYing-Chun Liu (PaulLiu) 
3610503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_HOSTSTATUS,
3620503adf4SYing-Chun Liu (PaulLiu) 			      HC_HSTST_MASK_ERROR_ALL);
3630503adf4SYing-Chun Liu (PaulLiu) 
3642c8ef2aeSYing-Chun Liu (PaulLiu) 		/*
3652c8ef2aeSYing-Chun Liu (PaulLiu) 		 * If the command SEND_OP_COND returns with CRC7 error,
3662c8ef2aeSYing-Chun Liu (PaulLiu) 		 * it can be considered as having completed successfully.
3672c8ef2aeSYing-Chun Liu (PaulLiu) 		 */
3680503adf4SYing-Chun Liu (PaulLiu) 		if (!(sdhsts & HC_HSTST_ERROR_CRC7)
3692c8ef2aeSYing-Chun Liu (PaulLiu) 		    || (cmd_idx != MMC_CMD(1))) {
3700503adf4SYing-Chun Liu (PaulLiu) 			if (sdhsts & HC_HSTST_TIMEOUT_CMD) {
3710503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost: timeout status 0x%x\n",
3720503adf4SYing-Chun Liu (PaulLiu) 				      sdhsts);
3730503adf4SYing-Chun Liu (PaulLiu) 				err = -(ETIMEDOUT);
3740503adf4SYing-Chun Liu (PaulLiu) 			} else {
3750503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost: unknown err, cmd = 0x%x\n",
3760503adf4SYing-Chun Liu (PaulLiu) 				      mmio_read_32(reg_base + HC_COMMAND));
3770503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost status: 0x%x\n", sdhsts);
3780503adf4SYing-Chun Liu (PaulLiu) 				err = -(EILSEQ);
3790503adf4SYing-Chun Liu (PaulLiu) 			}
3800503adf4SYing-Chun Liu (PaulLiu) 		}
3810503adf4SYing-Chun Liu (PaulLiu) 	}
3820503adf4SYing-Chun Liu (PaulLiu) 
3830503adf4SYing-Chun Liu (PaulLiu) 	if ((!err) && (cmd_idx == MMC_CMD(3))) {
3840503adf4SYing-Chun Liu (PaulLiu) 		/* we keep the RCA in case to send MMC_CMD(55) ourselves */
3850503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.sdcard_rca = (cmd->resp_data[0]
3860503adf4SYing-Chun Liu (PaulLiu) 						 & 0xFFFF0000U) >> 16;
3870503adf4SYing-Chun Liu (PaulLiu) 	}
3880503adf4SYing-Chun Liu (PaulLiu) 
3890503adf4SYing-Chun Liu (PaulLiu) 	return err;
3900503adf4SYing-Chun Liu (PaulLiu) }
3910503adf4SYing-Chun Liu (PaulLiu) 
3920503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_set_clock(unsigned int clk)
3930503adf4SYing-Chun Liu (PaulLiu) {
3940503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
3950503adf4SYing-Chun Liu (PaulLiu) 	uint32_t max_clk = 250000000;
3960503adf4SYing-Chun Liu (PaulLiu) 	uint32_t div;
3970503adf4SYing-Chun Liu (PaulLiu) 
3980503adf4SYing-Chun Liu (PaulLiu) 	if (clk < 100000) {
3990503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_CLOCKDIVISOR,
4000503adf4SYing-Chun Liu (PaulLiu) 			      HC_CLOCKDIVISOR_MAXVAL);
4010503adf4SYing-Chun Liu (PaulLiu) 		return 0;
4020503adf4SYing-Chun Liu (PaulLiu) 	}
4030503adf4SYing-Chun Liu (PaulLiu) 
4040503adf4SYing-Chun Liu (PaulLiu) 	div = max_clk / clk;
4050503adf4SYing-Chun Liu (PaulLiu) 	if (div < 2)
4060503adf4SYing-Chun Liu (PaulLiu) 		div = 2;
4070503adf4SYing-Chun Liu (PaulLiu) 
4080503adf4SYing-Chun Liu (PaulLiu) 	if ((max_clk / div) > clk)
4090503adf4SYing-Chun Liu (PaulLiu) 		div++;
4100503adf4SYing-Chun Liu (PaulLiu) 
4110503adf4SYing-Chun Liu (PaulLiu) 	div -= 2;
4120503adf4SYing-Chun Liu (PaulLiu) 	if (div > HC_CLOCKDIVISOR_MAXVAL)
4130503adf4SYing-Chun Liu (PaulLiu) 		div = HC_CLOCKDIVISOR_MAXVAL;
4140503adf4SYing-Chun Liu (PaulLiu) 
4150503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.clk_rate = max_clk / (div + 2);
4160503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.ns_per_fifo_word = (1000000000 /
4170503adf4SYing-Chun Liu (PaulLiu) 					       rpi3_sdhost_params.clk_rate)
4180503adf4SYing-Chun Liu (PaulLiu) 		* 8;
4190503adf4SYing-Chun Liu (PaulLiu) 
4200503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, div);
4210503adf4SYing-Chun Liu (PaulLiu) 	return 0;
4220503adf4SYing-Chun Liu (PaulLiu) }
4230503adf4SYing-Chun Liu (PaulLiu) 
4240503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width)
4250503adf4SYing-Chun Liu (PaulLiu) {
4260503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
4270503adf4SYing-Chun Liu (PaulLiu) 	uint32_t tmp1;
4280503adf4SYing-Chun Liu (PaulLiu) 
4290503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_set_clock(clk);
4300503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Changing clock to %dHz for data mode\n", clk);
4310503adf4SYing-Chun Liu (PaulLiu) 
4320503adf4SYing-Chun Liu (PaulLiu) 	if (width != MMC_BUS_WIDTH_4 && width != MMC_BUS_WIDTH_1) {
4330503adf4SYing-Chun Liu (PaulLiu) 		ERROR("rpi3_sdhost: width %d not supported\n", width);
4340503adf4SYing-Chun Liu (PaulLiu) 		return -(EOPNOTSUPP);
4350503adf4SYing-Chun Liu (PaulLiu) 	}
4360503adf4SYing-Chun Liu (PaulLiu) 	rpi3_sdhost_params.bus_width = width;
4370503adf4SYing-Chun Liu (PaulLiu) 
4380503adf4SYing-Chun Liu (PaulLiu) 	tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
4390503adf4SYing-Chun Liu (PaulLiu) 	tmp1 &= ~(HC_HSTCF_EXTBUS_4BIT);
4400503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.bus_width == MMC_BUS_WIDTH_4)
4410503adf4SYing-Chun Liu (PaulLiu) 		tmp1 |= HC_HSTCF_EXTBUS_4BIT;
4420503adf4SYing-Chun Liu (PaulLiu) 
4430503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1);
4440503adf4SYing-Chun Liu (PaulLiu) 	tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
4450503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 |
4460503adf4SYing-Chun Liu (PaulLiu) 		      HC_HSTCF_SLOW_CARD | HC_HSTCF_INTBUS_WIDE);
4470503adf4SYing-Chun Liu (PaulLiu) 
4480503adf4SYing-Chun Liu (PaulLiu) 	return 0;
4490503adf4SYing-Chun Liu (PaulLiu) }
4500503adf4SYing-Chun Liu (PaulLiu) 
4510503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size)
4520503adf4SYing-Chun Liu (PaulLiu) {
4530503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
4540503adf4SYing-Chun Liu (PaulLiu) 	size_t blocks;
4550503adf4SYing-Chun Liu (PaulLiu) 	size_t blocksize;
4560503adf4SYing-Chun Liu (PaulLiu) 
4570503adf4SYing-Chun Liu (PaulLiu) 	if (size < 512) {
4580503adf4SYing-Chun Liu (PaulLiu) 		blocksize = size;
4590503adf4SYing-Chun Liu (PaulLiu) 		blocks = 1;
4600503adf4SYing-Chun Liu (PaulLiu) 	} else {
4610503adf4SYing-Chun Liu (PaulLiu) 		blocksize = 512;
4620503adf4SYing-Chun Liu (PaulLiu) 		blocks = size / blocksize;
4630503adf4SYing-Chun Liu (PaulLiu) 		if (size % blocksize != 0)
4640503adf4SYing-Chun Liu (PaulLiu) 			blocks++;
4650503adf4SYing-Chun Liu (PaulLiu) 	}
4660503adf4SYing-Chun Liu (PaulLiu) 
4670503adf4SYing-Chun Liu (PaulLiu) 	rpi3_drain_fifo();
4680503adf4SYing-Chun Liu (PaulLiu) 
4690503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKSIZE, blocksize);
4700503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKCOUNT, blocks);
4710503adf4SYing-Chun Liu (PaulLiu) 	udelay(100);
4720503adf4SYing-Chun Liu (PaulLiu) 	return 0;
4730503adf4SYing-Chun Liu (PaulLiu) }
4740503adf4SYing-Chun Liu (PaulLiu) 
4750503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size)
4760503adf4SYing-Chun Liu (PaulLiu) {
4770503adf4SYing-Chun Liu (PaulLiu) 	int err = 0;
4780503adf4SYing-Chun Liu (PaulLiu) 	uint32_t *buf1 = ((uint32_t *) buf);
4790503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
4800503adf4SYing-Chun Liu (PaulLiu) 	int timeout = 100000;
4810503adf4SYing-Chun Liu (PaulLiu) 	int remaining_words = 0;
4820503adf4SYing-Chun Liu (PaulLiu) 
4830503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 0; i < size / 4; i++) {
4840503adf4SYing-Chun Liu (PaulLiu) 		volatile int t = timeout;
4850503adf4SYing-Chun Liu (PaulLiu) 		uint32_t hsts_err;
4860503adf4SYing-Chun Liu (PaulLiu) 
4870503adf4SYing-Chun Liu (PaulLiu) 		while ((mmio_read_32(reg_base + HC_HOSTSTATUS)
4880503adf4SYing-Chun Liu (PaulLiu) 			& HC_HSTST_HAVEDATA) == 0) {
4890503adf4SYing-Chun Liu (PaulLiu) 			if (t == 0) {
4900503adf4SYing-Chun Liu (PaulLiu) 				ERROR("rpi3_sdhost: fifo timeout after %dus\n",
4910503adf4SYing-Chun Liu (PaulLiu) 				      timeout);
4920503adf4SYing-Chun Liu (PaulLiu) 				err = -(ETIMEDOUT);
4930503adf4SYing-Chun Liu (PaulLiu) 				break;
4940503adf4SYing-Chun Liu (PaulLiu) 			}
4950503adf4SYing-Chun Liu (PaulLiu) 			t--;
4960503adf4SYing-Chun Liu (PaulLiu) 			udelay(10);
4970503adf4SYing-Chun Liu (PaulLiu) 		}
4980503adf4SYing-Chun Liu (PaulLiu) 		if (t == 0)
4990503adf4SYing-Chun Liu (PaulLiu) 			break;
5000503adf4SYing-Chun Liu (PaulLiu) 
5010503adf4SYing-Chun Liu (PaulLiu) 		uint32_t data = mmio_read_32(reg_base + HC_DATAPORT);
5020503adf4SYing-Chun Liu (PaulLiu) 
5030503adf4SYing-Chun Liu (PaulLiu) 		hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
5040503adf4SYing-Chun Liu (PaulLiu) 			& HC_HSTST_MASK_ERROR_ALL;
5050503adf4SYing-Chun Liu (PaulLiu) 		if (hsts_err) {
5060503adf4SYing-Chun Liu (PaulLiu) 			ERROR("rpi3_sdhost: transfer FIFO word %d: 0x%x\n",
5070503adf4SYing-Chun Liu (PaulLiu) 			      i,
5080503adf4SYing-Chun Liu (PaulLiu) 			      mmio_read_32(reg_base + HC_HOSTSTATUS));
5090503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_print_regs();
5100503adf4SYing-Chun Liu (PaulLiu) 
5110503adf4SYing-Chun Liu (PaulLiu) 			err = -(EILSEQ);
5120503adf4SYing-Chun Liu (PaulLiu) 
5130503adf4SYing-Chun Liu (PaulLiu) 			/* clean the error status */
5140503adf4SYing-Chun Liu (PaulLiu) 			mmio_write_32(reg_base + HC_HOSTSTATUS, hsts_err);
5150503adf4SYing-Chun Liu (PaulLiu) 		}
5160503adf4SYing-Chun Liu (PaulLiu) 
5170503adf4SYing-Chun Liu (PaulLiu) 		if (buf1)
5180503adf4SYing-Chun Liu (PaulLiu) 			buf1[i] = data;
5190503adf4SYing-Chun Liu (PaulLiu) 
5200503adf4SYing-Chun Liu (PaulLiu) 		/* speeding up if the remaining words are still a lot */
5210503adf4SYing-Chun Liu (PaulLiu) 		remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
5220503adf4SYing-Chun Liu (PaulLiu) 			& HC_DBG_FIFO_THRESH_MASK;
5230503adf4SYing-Chun Liu (PaulLiu) 		if (remaining_words >= 7)
5240503adf4SYing-Chun Liu (PaulLiu) 			continue;
5250503adf4SYing-Chun Liu (PaulLiu) 
5260503adf4SYing-Chun Liu (PaulLiu) 		/* delay. slowing down the read process */
5270503adf4SYing-Chun Liu (PaulLiu) 		udelay(100);
5280503adf4SYing-Chun Liu (PaulLiu) 	}
5290503adf4SYing-Chun Liu (PaulLiu) 
5300503adf4SYing-Chun Liu (PaulLiu) 	/* We decide to stop by ourselves.
5310503adf4SYing-Chun Liu (PaulLiu) 	 * It is because MMC_CMD(18) -> MMC_CMD(13) -> MMC_CMD(12)
5320503adf4SYing-Chun Liu (PaulLiu) 	 * doesn't work for RPi3 SDHost.
5330503adf4SYing-Chun Liu (PaulLiu) 	 */
5340503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.current_cmd == MMC_CMD(18))
5350503adf4SYing-Chun Liu (PaulLiu) 		send_command_decorated(MMC_CMD(12), 0);
5360503adf4SYing-Chun Liu (PaulLiu) 
5370503adf4SYing-Chun Liu (PaulLiu) 	return err;
5380503adf4SYing-Chun Liu (PaulLiu) }
5390503adf4SYing-Chun Liu (PaulLiu) 
5400503adf4SYing-Chun Liu (PaulLiu) static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size)
5410503adf4SYing-Chun Liu (PaulLiu) {
5420503adf4SYing-Chun Liu (PaulLiu) 	uint32_t *buf1 = ((uint32_t *) buf);
5430503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
5440503adf4SYing-Chun Liu (PaulLiu) 	int err = 0;
5450503adf4SYing-Chun Liu (PaulLiu) 	int remaining_words = 0;
5460503adf4SYing-Chun Liu (PaulLiu) 
5470503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 0; i < size / 4; i++) {
5480503adf4SYing-Chun Liu (PaulLiu) 		uint32_t hsts_err;
5490503adf4SYing-Chun Liu (PaulLiu) 		uint32_t data = buf1[i];
5500503adf4SYing-Chun Liu (PaulLiu) 		uint32_t dbg;
5510503adf4SYing-Chun Liu (PaulLiu) 		uint32_t fsm_state;
5520503adf4SYing-Chun Liu (PaulLiu) 
5530503adf4SYing-Chun Liu (PaulLiu) 		mmio_write_32(reg_base + HC_DATAPORT, data);
5540503adf4SYing-Chun Liu (PaulLiu) 
5550503adf4SYing-Chun Liu (PaulLiu) 		dbg = mmio_read_32(reg_base + HC_DEBUG);
5560503adf4SYing-Chun Liu (PaulLiu) 		fsm_state = dbg & HC_DBG_FSM_MASK;
5570503adf4SYing-Chun Liu (PaulLiu) 		if (fsm_state != HC_DBG_FSM_WRITEDATA
5580503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITESTART1
5590503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITESTART2
5600503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITECRC
5610503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITEWAIT1
5620503adf4SYing-Chun Liu (PaulLiu) 		    && fsm_state != HC_DBG_FSM_WRITEWAIT2) {
5630503adf4SYing-Chun Liu (PaulLiu) 			hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
5640503adf4SYing-Chun Liu (PaulLiu) 				& HC_HSTST_MASK_ERROR_ALL;
5650503adf4SYing-Chun Liu (PaulLiu) 			if (hsts_err)
5660503adf4SYing-Chun Liu (PaulLiu) 				err = -(EILSEQ);
5670503adf4SYing-Chun Liu (PaulLiu) 		}
5680503adf4SYing-Chun Liu (PaulLiu) 
5690503adf4SYing-Chun Liu (PaulLiu) 		/* speeding up if the remaining words are not many */
5700503adf4SYing-Chun Liu (PaulLiu) 		remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
5710503adf4SYing-Chun Liu (PaulLiu) 			& HC_DBG_FIFO_THRESH_MASK;
5720503adf4SYing-Chun Liu (PaulLiu) 		if (remaining_words <= 4)
5730503adf4SYing-Chun Liu (PaulLiu) 			continue;
5740503adf4SYing-Chun Liu (PaulLiu) 
5750503adf4SYing-Chun Liu (PaulLiu) 		udelay(100);
5760503adf4SYing-Chun Liu (PaulLiu) 	}
5770503adf4SYing-Chun Liu (PaulLiu) 
5780503adf4SYing-Chun Liu (PaulLiu) 	/* We decide to stop by ourselves.
5790503adf4SYing-Chun Liu (PaulLiu) 	 * It is because MMC_CMD(25) -> MMC_CMD(13) -> MMC_CMD(12)
5800503adf4SYing-Chun Liu (PaulLiu) 	 * doesn't work for RPi3 SDHost.
5810503adf4SYing-Chun Liu (PaulLiu) 	 */
5820503adf4SYing-Chun Liu (PaulLiu) 	if (rpi3_sdhost_params.current_cmd == MMC_CMD(25))
5830503adf4SYing-Chun Liu (PaulLiu) 		send_command_decorated(MMC_CMD(12), 0);
5840503adf4SYing-Chun Liu (PaulLiu) 
5850503adf4SYing-Chun Liu (PaulLiu) 	return err;
5860503adf4SYing-Chun Liu (PaulLiu) }
5870503adf4SYing-Chun Liu (PaulLiu) 
5880503adf4SYing-Chun Liu (PaulLiu) void rpi3_sdhost_init(struct rpi3_sdhost_params *params,
5890503adf4SYing-Chun Liu (PaulLiu) 		    struct mmc_device_info *mmc_dev_info)
5900503adf4SYing-Chun Liu (PaulLiu) {
5910503adf4SYing-Chun Liu (PaulLiu) 	assert((params != 0) &&
5920503adf4SYing-Chun Liu (PaulLiu) 	       ((params->reg_base & MMC_BLOCK_MASK) == 0));
5930503adf4SYing-Chun Liu (PaulLiu) 
5940503adf4SYing-Chun Liu (PaulLiu) 	memcpy(&rpi3_sdhost_params, params, sizeof(struct rpi3_sdhost_params));
5950503adf4SYing-Chun Liu (PaulLiu) 
5960503adf4SYing-Chun Liu (PaulLiu) 	/* backup GPIO 48 to 53 configurations */
5970503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++) {
5980503adf4SYing-Chun Liu (PaulLiu) 		rpi3_sdhost_params.gpio48_pinselect[i - 48]
5990503adf4SYing-Chun Liu (PaulLiu) 			= rpi3_gpio_get_select(i);
6000503adf4SYing-Chun Liu (PaulLiu) 		VERBOSE("rpi3_sdhost: Original GPIO state %d: %d\n",
6010503adf4SYing-Chun Liu (PaulLiu) 			i,
6020503adf4SYing-Chun Liu (PaulLiu) 			rpi3_sdhost_params.gpio48_pinselect[i - 48]);
6030503adf4SYing-Chun Liu (PaulLiu) 	}
6040503adf4SYing-Chun Liu (PaulLiu) 
6050503adf4SYing-Chun Liu (PaulLiu) 	/* setting pull resistors for 48 to 53.
6062c8ef2aeSYing-Chun Liu (PaulLiu) 	 * It is debatable to set SD_CLK to UP or NONE. We massively
6072c8ef2aeSYing-Chun Liu (PaulLiu) 	 * tested different brands of SD Cards and found NONE works
6082c8ef2aeSYing-Chun Liu (PaulLiu) 	 * most stable.
6092c8ef2aeSYing-Chun Liu (PaulLiu) 	 *
6102c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 48 (SD_CLK) to GPIO_PULL_NONE
6112c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 49 (SD_CMD) to GPIO_PULL_UP
6122c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 50 (SD_D0)  to GPIO_PULL_UP
6132c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 51 (SD_D1)  to GPIO_PULL_UP
6142c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 52 (SD_D2)  to GPIO_PULL_UP
6152c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 53 (SD_D3)  to GPIO_PULL_UP
6160503adf4SYing-Chun Liu (PaulLiu) 	 */
6172c8ef2aeSYing-Chun Liu (PaulLiu) 	gpio_set_pull(48, GPIO_PULL_NONE);
6180503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 49; i <= 53; i++)
6192c8ef2aeSYing-Chun Liu (PaulLiu) 		gpio_set_pull(i, GPIO_PULL_UP);
6200503adf4SYing-Chun Liu (PaulLiu) 
6210503adf4SYing-Chun Liu (PaulLiu) 	/* Set pin 48-53 to alt-0. It means route SDHOST to card slot */
6220503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++)
6230503adf4SYing-Chun Liu (PaulLiu) 		rpi3_gpio_set_select(i, RPI3_GPIO_FUNC_ALT0);
6240503adf4SYing-Chun Liu (PaulLiu) 
6250503adf4SYing-Chun Liu (PaulLiu) 	mmc_init(&rpi3_sdhost_ops, params->clk_rate, params->bus_width,
6260503adf4SYing-Chun Liu (PaulLiu) 		 params->flags, mmc_dev_info);
6270503adf4SYing-Chun Liu (PaulLiu) }
6280503adf4SYing-Chun Liu (PaulLiu) 
6290503adf4SYing-Chun Liu (PaulLiu) void rpi3_sdhost_stop(void)
6300503adf4SYing-Chun Liu (PaulLiu) {
6310503adf4SYing-Chun Liu (PaulLiu) 	uintptr_t reg_base = rpi3_sdhost_params.reg_base;
6320503adf4SYing-Chun Liu (PaulLiu) 
6330503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Shutting down: drain FIFO out\n");
6340503adf4SYing-Chun Liu (PaulLiu) 	rpi3_drain_fifo();
6350503adf4SYing-Chun Liu (PaulLiu) 
6360503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Shutting down: slowing down the clock\n");
6370503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base+HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_SLOWVAL);
6380503adf4SYing-Chun Liu (PaulLiu) 	udelay(500);
6390503adf4SYing-Chun Liu (PaulLiu) 
6400503adf4SYing-Chun Liu (PaulLiu) 	VERBOSE("rpi3_sdhost: Shutting down: put SDHost into idle state\n");
6410503adf4SYing-Chun Liu (PaulLiu) 	send_command_decorated(MMC_CMD(0), 0);
6420503adf4SYing-Chun Liu (PaulLiu) 	udelay(500);
6430503adf4SYing-Chun Liu (PaulLiu) 
6440503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_COMMAND, 0);
6450503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_ARGUMENT, 0);
6460503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_IDLE);
6470503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_STOPVAL);
6480503adf4SYing-Chun Liu (PaulLiu) 
6490503adf4SYing-Chun Liu (PaulLiu) 	udelay(100);
6500503adf4SYing-Chun Liu (PaulLiu) 
6510503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_POWER, 0);
6520503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
6530503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKSIZE, 0x400);
6540503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
6550503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_HOSTSTATUS, 0x7f8);
6560503adf4SYing-Chun Liu (PaulLiu) 
6570503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_COMMAND, 0);
6580503adf4SYing-Chun Liu (PaulLiu) 	mmio_write_32(reg_base + HC_ARGUMENT, 0);
6590503adf4SYing-Chun Liu (PaulLiu) 
6600503adf4SYing-Chun Liu (PaulLiu) 	udelay(100);
6610503adf4SYing-Chun Liu (PaulLiu) 
6620503adf4SYing-Chun Liu (PaulLiu) 	/* Restore the pinmux to original state */
6630503adf4SYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++) {
6640503adf4SYing-Chun Liu (PaulLiu) 		rpi3_gpio_set_select(i,
6650503adf4SYing-Chun Liu (PaulLiu) 				     rpi3_sdhost_params.gpio48_pinselect[i-48]);
6660503adf4SYing-Chun Liu (PaulLiu) 	}
6670503adf4SYing-Chun Liu (PaulLiu) 
6682c8ef2aeSYing-Chun Liu (PaulLiu) 	/* Reset the pull resistors before entering BL33.
6692c8ef2aeSYing-Chun Liu (PaulLiu) 	 * GPIO 48 (SD_CLK) to GPIO_PULL_UP
6700503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 49 (SD_CMD) to GPIO_PULL_UP
6710503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 50 (SD_D0)  to GPIO_PULL_UP
6720503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 51 (SD_D1)  to GPIO_PULL_UP
6730503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 52 (SD_D2)  to GPIO_PULL_UP
6740503adf4SYing-Chun Liu (PaulLiu) 	 * GPIO 53 (SD_D3)  to GPIO_PULL_UP
6750503adf4SYing-Chun Liu (PaulLiu) 	 */
6762c8ef2aeSYing-Chun Liu (PaulLiu) 	for (int i = 48; i <= 53; i++)
6770503adf4SYing-Chun Liu (PaulLiu) 		gpio_set_pull(i, GPIO_PULL_UP);
6780503adf4SYing-Chun Liu (PaulLiu) }
679