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