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) */
rpi3_sdhost_waitcommand(void)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) */
send_command_raw(unsigned int cmd,unsigned int arg)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) */
send_command_decorated(unsigned int cmd,unsigned int arg)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) */
rpi3_drain_fifo(void)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) */
rpi3_sdhost_print_regs(void)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) */
rpi3_sdhost_reset(void)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)
rpi3_sdhost_initialize(void)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)
rpi3_sdhost_send_cmd(struct mmc_cmd * cmd)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)
rpi3_sdhost_set_clock(unsigned int clk)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)
rpi3_sdhost_set_ios(unsigned int clk,unsigned int width)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)
rpi3_sdhost_prepare(int lba,uintptr_t buf,size_t size)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)
rpi3_sdhost_read(int lba,uintptr_t buf,size_t size)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)
rpi3_sdhost_write(int lba,uintptr_t buf,size_t size)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)
rpi3_sdhost_init(struct rpi3_sdhost_params * params,struct mmc_device_info * mmc_dev_info)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)
rpi3_sdhost_stop(void)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