18b659130SJun Nie /* 28b659130SJun Nie * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. 38b659130SJun Nie * 48b659130SJun Nie * SPDX-License-Identifier: BSD-3-Clause 58b659130SJun Nie */ 68b659130SJun Nie 7*09d40e0eSAntonio Nino Diaz #include <assert.h> 8*09d40e0eSAntonio Nino Diaz #include <errno.h> 9*09d40e0eSAntonio Nino Diaz #include <string.h> 10*09d40e0eSAntonio Nino Diaz 118b659130SJun Nie #include <arch.h> 128b659130SJun Nie #include <arch_helpers.h> 13*09d40e0eSAntonio Nino Diaz #include <common/debug.h> 14*09d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h> 15*09d40e0eSAntonio Nino Diaz #include <drivers/mmc.h> 16*09d40e0eSAntonio Nino Diaz #include <lib/mmio.h> 17*09d40e0eSAntonio Nino Diaz 188b659130SJun Nie #include <imx_usdhc.h> 198b659130SJun Nie 208b659130SJun Nie static void imx_usdhc_initialize(void); 218b659130SJun Nie static int imx_usdhc_send_cmd(struct mmc_cmd *cmd); 228b659130SJun Nie static int imx_usdhc_set_ios(unsigned int clk, unsigned int width); 238b659130SJun Nie static int imx_usdhc_prepare(int lba, uintptr_t buf, size_t size); 248b659130SJun Nie static int imx_usdhc_read(int lba, uintptr_t buf, size_t size); 258b659130SJun Nie static int imx_usdhc_write(int lba, uintptr_t buf, size_t size); 268b659130SJun Nie 278b659130SJun Nie static const struct mmc_ops imx_usdhc_ops = { 288b659130SJun Nie .init = imx_usdhc_initialize, 298b659130SJun Nie .send_cmd = imx_usdhc_send_cmd, 308b659130SJun Nie .set_ios = imx_usdhc_set_ios, 318b659130SJun Nie .prepare = imx_usdhc_prepare, 328b659130SJun Nie .read = imx_usdhc_read, 338b659130SJun Nie .write = imx_usdhc_write, 348b659130SJun Nie }; 358b659130SJun Nie 368b659130SJun Nie static imx_usdhc_params_t imx_usdhc_params; 378b659130SJun Nie 388b659130SJun Nie #define IMX7_MMC_SRC_CLK_RATE (200 * 1000 * 1000) 398b659130SJun Nie static void imx_usdhc_set_clk(int clk) 408b659130SJun Nie { 418b659130SJun Nie int div = 1; 428b659130SJun Nie int pre_div = 1; 438b659130SJun Nie unsigned int sdhc_clk = IMX7_MMC_SRC_CLK_RATE; 448b659130SJun Nie uintptr_t reg_base = imx_usdhc_params.reg_base; 458b659130SJun Nie 468b659130SJun Nie assert(clk > 0); 478b659130SJun Nie 488b659130SJun Nie while (sdhc_clk / (16 * pre_div) > clk && pre_div < 256) 498b659130SJun Nie pre_div *= 2; 508b659130SJun Nie 518b659130SJun Nie while (sdhc_clk / div > clk && div < 16) 528b659130SJun Nie div++; 538b659130SJun Nie 548b659130SJun Nie pre_div >>= 1; 558b659130SJun Nie div -= 1; 568b659130SJun Nie clk = (pre_div << 8) | (div << 4); 578b659130SJun Nie 588b659130SJun Nie mmio_clrbits32(reg_base + VENDSPEC, VENDSPEC_CARD_CLKEN); 598b659130SJun Nie mmio_clrsetbits32(reg_base + SYSCTRL, SYSCTRL_CLOCK_MASK, clk); 608b659130SJun Nie udelay(10000); 618b659130SJun Nie 628b659130SJun Nie mmio_setbits32(reg_base + VENDSPEC, VENDSPEC_PER_CLKEN | VENDSPEC_CARD_CLKEN); 638b659130SJun Nie } 648b659130SJun Nie 658b659130SJun Nie static void imx_usdhc_initialize(void) 668b659130SJun Nie { 678b659130SJun Nie unsigned int timeout = 10000; 688b659130SJun Nie uintptr_t reg_base = imx_usdhc_params.reg_base; 698b659130SJun Nie 708b659130SJun Nie assert((imx_usdhc_params.reg_base & MMC_BLOCK_MASK) == 0); 718b659130SJun Nie 728b659130SJun Nie /* reset the controller */ 738b659130SJun Nie mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTA); 748b659130SJun Nie 758b659130SJun Nie /* wait for reset done */ 768b659130SJun Nie while ((mmio_read_32(reg_base + SYSCTRL) & SYSCTRL_RSTA)) { 778b659130SJun Nie if (!timeout) 788b659130SJun Nie ERROR("IMX MMC reset timeout.\n"); 798b659130SJun Nie timeout--; 808b659130SJun Nie } 818b659130SJun Nie 828b659130SJun Nie mmio_write_32(reg_base + MMCBOOT, 0); 838b659130SJun Nie mmio_write_32(reg_base + MIXCTRL, 0); 848b659130SJun Nie mmio_write_32(reg_base + CLKTUNECTRLSTS, 0); 858b659130SJun Nie 868b659130SJun Nie mmio_write_32(reg_base + VENDSPEC, VENDSPEC_INIT); 878b659130SJun Nie mmio_write_32(reg_base + DLLCTRL, 0); 888b659130SJun Nie mmio_setbits32(reg_base + VENDSPEC, VENDSPEC_IPG_CLKEN | VENDSPEC_PER_CLKEN); 898b659130SJun Nie 908b659130SJun Nie /* Set the initial boot clock rate */ 918b659130SJun Nie imx_usdhc_set_clk(MMC_BOOT_CLK_RATE); 928b659130SJun Nie udelay(100); 938b659130SJun Nie 948b659130SJun Nie /* Clear read/write ready status */ 958b659130SJun Nie mmio_clrbits32(reg_base + INTSTATEN, INTSTATEN_BRR | INTSTATEN_BWR); 968b659130SJun Nie 978b659130SJun Nie /* configure as little endian */ 988b659130SJun Nie mmio_write_32(reg_base + PROTCTRL, PROTCTRL_LE); 998b659130SJun Nie 1008b659130SJun Nie /* Set timeout to the maximum value */ 1018b659130SJun Nie mmio_clrsetbits32(reg_base + SYSCTRL, SYSCTRL_TIMEOUT_MASK, 1028b659130SJun Nie SYSCTRL_TIMEOUT(15)); 1038b659130SJun Nie 1048b659130SJun Nie /* set wartermark level as 16 for safe for MMC */ 1058b659130SJun Nie mmio_clrsetbits32(reg_base + WATERMARKLEV, WMKLV_MASK, 16 | (16 << 16)); 1068b659130SJun Nie } 1078b659130SJun Nie 1088b659130SJun Nie #define FSL_CMD_RETRIES 1000 1098b659130SJun Nie 1108b659130SJun Nie static int imx_usdhc_send_cmd(struct mmc_cmd *cmd) 1118b659130SJun Nie { 1128b659130SJun Nie uintptr_t reg_base = imx_usdhc_params.reg_base; 1138b659130SJun Nie unsigned int xfertype = 0, mixctl = 0, multiple = 0, data = 0, err = 0; 1148b659130SJun Nie unsigned int state, flags = INTSTATEN_CC | INTSTATEN_CTOE; 1158b659130SJun Nie unsigned int cmd_retries = 0; 1168b659130SJun Nie 1178b659130SJun Nie assert(cmd); 1188b659130SJun Nie 1198b659130SJun Nie /* clear all irq status */ 1208b659130SJun Nie mmio_write_32(reg_base + INTSTAT, 0xffffffff); 1218b659130SJun Nie 1228b659130SJun Nie /* Wait for the bus to be idle */ 1238b659130SJun Nie do { 1248b659130SJun Nie state = mmio_read_32(reg_base + PSTATE); 1258b659130SJun Nie } while (state & (PSTATE_CDIHB | PSTATE_CIHB)); 1268b659130SJun Nie 1278b659130SJun Nie while (mmio_read_32(reg_base + PSTATE) & PSTATE_DLA) 1288b659130SJun Nie ; 1298b659130SJun Nie 1308b659130SJun Nie mmio_write_32(reg_base + INTSIGEN, 0); 1318b659130SJun Nie udelay(1000); 1328b659130SJun Nie 1338b659130SJun Nie switch (cmd->cmd_idx) { 1348b659130SJun Nie case MMC_CMD(12): 1358b659130SJun Nie xfertype |= XFERTYPE_CMDTYP_ABORT; 1368b659130SJun Nie break; 1378b659130SJun Nie case MMC_CMD(18): 1388b659130SJun Nie multiple = 1; 1398b659130SJun Nie /* fall thru for read op */ 1408b659130SJun Nie case MMC_CMD(17): 1418b659130SJun Nie case MMC_CMD(8): 1428b659130SJun Nie mixctl |= MIXCTRL_DTDSEL; 1438b659130SJun Nie data = 1; 1448b659130SJun Nie break; 1458b659130SJun Nie case MMC_CMD(25): 1468b659130SJun Nie multiple = 1; 1478b659130SJun Nie /* fall thru for data op flag */ 1488b659130SJun Nie case MMC_CMD(24): 1498b659130SJun Nie data = 1; 1508b659130SJun Nie break; 1518b659130SJun Nie default: 1528b659130SJun Nie break; 1538b659130SJun Nie } 1548b659130SJun Nie 1558b659130SJun Nie if (multiple) { 1568b659130SJun Nie mixctl |= MIXCTRL_MSBSEL; 1578b659130SJun Nie mixctl |= MIXCTRL_BCEN; 1588b659130SJun Nie } 1598b659130SJun Nie 1608b659130SJun Nie if (data) { 1618b659130SJun Nie xfertype |= XFERTYPE_DPSEL; 1628b659130SJun Nie mixctl |= MIXCTRL_DMAEN; 1638b659130SJun Nie } 1648b659130SJun Nie 165a21da478SBryan O'Donoghue if (cmd->resp_type & MMC_RSP_48 && cmd->resp_type != MMC_RESPONSE_R2) 1668b659130SJun Nie xfertype |= XFERTYPE_RSPTYP_48; 1678b659130SJun Nie else if (cmd->resp_type & MMC_RSP_136) 1688b659130SJun Nie xfertype |= XFERTYPE_RSPTYP_136; 1698b659130SJun Nie else if (cmd->resp_type & MMC_RSP_BUSY) 1708b659130SJun Nie xfertype |= XFERTYPE_RSPTYP_48_BUSY; 1718b659130SJun Nie 1728b659130SJun Nie if (cmd->resp_type & MMC_RSP_CMD_IDX) 1738b659130SJun Nie xfertype |= XFERTYPE_CICEN; 1748b659130SJun Nie 1758b659130SJun Nie if (cmd->resp_type & MMC_RSP_CRC) 1768b659130SJun Nie xfertype |= XFERTYPE_CCCEN; 1778b659130SJun Nie 1788b659130SJun Nie xfertype |= XFERTYPE_CMD(cmd->cmd_idx); 1798b659130SJun Nie 1808b659130SJun Nie /* Send the command */ 1818b659130SJun Nie mmio_write_32(reg_base + CMDARG, cmd->cmd_arg); 1828b659130SJun Nie mmio_clrsetbits32(reg_base + MIXCTRL, MIXCTRL_DATMASK, mixctl); 1838b659130SJun Nie mmio_write_32(reg_base + XFERTYPE, xfertype); 1848b659130SJun Nie 1858b659130SJun Nie /* Wait for the command done */ 1868b659130SJun Nie do { 1878b659130SJun Nie state = mmio_read_32(reg_base + INTSTAT); 1888b659130SJun Nie if (cmd_retries) 1898b659130SJun Nie udelay(1); 1908b659130SJun Nie } while ((!(state & flags)) && ++cmd_retries < FSL_CMD_RETRIES); 1918b659130SJun Nie 1928b659130SJun Nie if ((state & (INTSTATEN_CTOE | CMD_ERR)) || cmd_retries == FSL_CMD_RETRIES) { 1938b659130SJun Nie if (cmd_retries == FSL_CMD_RETRIES) 1948b659130SJun Nie err = -ETIMEDOUT; 1958b659130SJun Nie else 1968b659130SJun Nie err = -EIO; 1978b659130SJun Nie ERROR("imx_usdhc mmc cmd %d state 0x%x errno=%d\n", 1988b659130SJun Nie cmd->cmd_idx, state, err); 1998b659130SJun Nie goto out; 2008b659130SJun Nie } 2018b659130SJun Nie 2028b659130SJun Nie /* Copy the response to the response buffer */ 2038b659130SJun Nie if (cmd->resp_type & MMC_RSP_136) { 2048b659130SJun Nie unsigned int cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; 2058b659130SJun Nie 2068b659130SJun Nie cmdrsp3 = mmio_read_32(reg_base + CMDRSP3); 2078b659130SJun Nie cmdrsp2 = mmio_read_32(reg_base + CMDRSP2); 2088b659130SJun Nie cmdrsp1 = mmio_read_32(reg_base + CMDRSP1); 2098b659130SJun Nie cmdrsp0 = mmio_read_32(reg_base + CMDRSP0); 2108b659130SJun Nie cmd->resp_data[3] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); 2118b659130SJun Nie cmd->resp_data[2] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); 2128b659130SJun Nie cmd->resp_data[1] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); 2138b659130SJun Nie cmd->resp_data[0] = (cmdrsp0 << 8); 2148b659130SJun Nie } else { 2158b659130SJun Nie cmd->resp_data[0] = mmio_read_32(reg_base + CMDRSP0); 2168b659130SJun Nie } 2178b659130SJun Nie 2188b659130SJun Nie /* Wait until all of the blocks are transferred */ 2198b659130SJun Nie if (data) { 2208b659130SJun Nie flags = DATA_COMPLETE; 2218b659130SJun Nie do { 2228b659130SJun Nie state = mmio_read_32(reg_base + INTSTAT); 2238b659130SJun Nie 2248b659130SJun Nie if (state & (INTSTATEN_DTOE | DATA_ERR)) { 2258b659130SJun Nie err = -EIO; 2268b659130SJun Nie ERROR("imx_usdhc mmc data state 0x%x\n", state); 2278b659130SJun Nie goto out; 2288b659130SJun Nie } 2298b659130SJun Nie } while ((state & flags) != flags); 2308b659130SJun Nie } 2318b659130SJun Nie 2328b659130SJun Nie out: 2338b659130SJun Nie /* Reset CMD and DATA on error */ 2348b659130SJun Nie if (err) { 2358b659130SJun Nie mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTC); 2368b659130SJun Nie while (mmio_read_32(reg_base + SYSCTRL) & SYSCTRL_RSTC) 2378b659130SJun Nie ; 2388b659130SJun Nie 2398b659130SJun Nie if (data) { 2408b659130SJun Nie mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTD); 2418b659130SJun Nie while (mmio_read_32(reg_base + SYSCTRL) & SYSCTRL_RSTD) 2428b659130SJun Nie ; 2438b659130SJun Nie } 2448b659130SJun Nie } 2458b659130SJun Nie 2468b659130SJun Nie /* clear all irq status */ 2478b659130SJun Nie mmio_write_32(reg_base + INTSTAT, 0xffffffff); 2488b659130SJun Nie 2498b659130SJun Nie return err; 2508b659130SJun Nie } 2518b659130SJun Nie 2528b659130SJun Nie static int imx_usdhc_set_ios(unsigned int clk, unsigned int width) 2538b659130SJun Nie { 2548b659130SJun Nie uintptr_t reg_base = imx_usdhc_params.reg_base; 2558b659130SJun Nie 2568b659130SJun Nie imx_usdhc_set_clk(clk); 2578b659130SJun Nie 2588b659130SJun Nie if (width == MMC_BUS_WIDTH_4) 2598b659130SJun Nie mmio_clrsetbits32(reg_base + PROTCTRL, PROTCTRL_WIDTH_MASK, 2608b659130SJun Nie PROTCTRL_WIDTH_4); 2618b659130SJun Nie else if (width == MMC_BUS_WIDTH_8) 2628b659130SJun Nie mmio_clrsetbits32(reg_base + PROTCTRL, PROTCTRL_WIDTH_MASK, 2638b659130SJun Nie PROTCTRL_WIDTH_8); 2648b659130SJun Nie 2658b659130SJun Nie return 0; 2668b659130SJun Nie } 2678b659130SJun Nie 2688b659130SJun Nie static int imx_usdhc_prepare(int lba, uintptr_t buf, size_t size) 2698b659130SJun Nie { 2708b659130SJun Nie uintptr_t reg_base = imx_usdhc_params.reg_base; 2718b659130SJun Nie 2728b659130SJun Nie mmio_write_32(reg_base + DSADDR, buf); 2738b659130SJun Nie mmio_write_32(reg_base + BLKATT, 2748b659130SJun Nie (size / MMC_BLOCK_SIZE) << 16 | MMC_BLOCK_SIZE); 2758b659130SJun Nie 2768b659130SJun Nie return 0; 2778b659130SJun Nie } 2788b659130SJun Nie 2798b659130SJun Nie static int imx_usdhc_read(int lba, uintptr_t buf, size_t size) 2808b659130SJun Nie { 2818b659130SJun Nie return 0; 2828b659130SJun Nie } 2838b659130SJun Nie 2848b659130SJun Nie static int imx_usdhc_write(int lba, uintptr_t buf, size_t size) 2858b659130SJun Nie { 2868b659130SJun Nie return 0; 2878b659130SJun Nie } 2888b659130SJun Nie 2898b659130SJun Nie void imx_usdhc_init(imx_usdhc_params_t *params, 2908b659130SJun Nie struct mmc_device_info *mmc_dev_info) 2918b659130SJun Nie { 2928b659130SJun Nie assert((params != 0) && 2938b659130SJun Nie ((params->reg_base & MMC_BLOCK_MASK) == 0) && 2948b659130SJun Nie (params->clk_rate > 0) && 2958b659130SJun Nie ((params->bus_width == MMC_BUS_WIDTH_1) || 2968b659130SJun Nie (params->bus_width == MMC_BUS_WIDTH_4) || 2978b659130SJun Nie (params->bus_width == MMC_BUS_WIDTH_8))); 2988b659130SJun Nie 2998b659130SJun Nie memcpy(&imx_usdhc_params, params, sizeof(imx_usdhc_params_t)); 3008b659130SJun Nie mmc_init(&imx_usdhc_ops, params->clk_rate, params->bus_width, 3018b659130SJun Nie params->flags, mmc_dev_info); 3028b659130SJun Nie } 303