/* * driver/mmc/nvt_ivot_mmc.c * * Author: Howard Chang * Created: Feb 01, 2019 * Copyright: Novatek Inc. * */ #include #include "common.h" #include #include #include #include #include #include #include "nvt_ivot_mmc.h" #ifdef CONFIG_TARGET_NA51055 #include #else #ifdef CONFIG_TARGET_NA51089 #include #else #if (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64)) #include #elif (defined(CONFIG_TARGET_NA51102) || defined(CONFIG_TARGET_NA51102_A64)) #include #elif (defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64)) #include #else #include #endif #endif #endif #include #include #include //#define __MMC_DEBUG //668 emmc HAL APIs //#define FPGA #define mmc_resp_type (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE) #if (defined(CONFIG_TARGET_NA51000) || defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51102) || defined(CONFIG_TARGET_NA51102_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64)) #define PLL_SYS_CR_REG_OFS 0xF0020040 #else #define PLL_SYS_CR_REG_OFS 0xF002003C #endif #if (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51102) || defined(CONFIG_TARGET_NA51102_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64)) #define SDIO_MASK 0x7FF #define SDIO2_MASK 0x7FF0000 #else #define SDIO_MASK 0x7FF #define SDIO2_MASK 0x7FF0000 //from 0x7FF000 to 0x7FF0000 for emmc speed added by greg #endif #ifdef CONFIG_NVT_FPGA_EMULATION #define FPGA_SDIO_SRCCLK 12000000 #else #define FPGA_SDIO_SRCCLK 192000000 #endif int mmc_nvt_start_command(struct mmc_nvt_host *host); #ifdef CONFIG_DM_MMC /* Novatek IVOT MMC board definitions */ struct nvt_mmc_plat { struct mmc_config cfg; struct mmc mmc; }; #endif static u32 default_pad_driving[SDIO_MAX_MODE_DRIVING] = {20, 15, 15, 20, 15, 15, 30, 25, 25, 40, 30, 30}; static void mmc_nvt_parse_driving(struct mmc_nvt_host *host) { /* Enable it after dts parsing ready*/ ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr); int nodeoffset; u32 *cell = NULL; char path[20] = {0}; int i; if (host->id == SDIO_HOST_ID_1) { sprintf(path,"/mmc@%x",IOADDR_SDIO_REG_BASE); } else if (host->id == SDIO_HOST_ID_2) { sprintf(path,"/mmc@%x",IOADDR_SDIO2_REG_BASE); } else if (host->id == SDIO_HOST_ID_3) { sprintf(path,"/mmc@%x",IOADDR_SDIO3_REG_BASE); } nodeoffset = fdt_path_offset((const void*)fdt_addr, path); cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "max-frequency", NULL); if (cell > 0) { host->mmc_default_clk = __be32_to_cpu(cell[0]); } cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "driving", NULL); if (cell < 0) { for (i = 0; i < SDIO_MAX_MODE_DRIVING; i++) { host->pad_driving[i] = default_pad_driving[i]; } printf("\n%s: use default driving table\n", __func__); } else { for (i = 0; i < SDIO_MAX_MODE_DRIVING; i++) { host->pad_driving[i] = __be32_to_cpu(cell[i]); } } cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "mmc-hs200-1_8v", NULL); if (cell > 0) host->ext_caps |= MMC_MODE_HS200; cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "indly_sel", NULL); if (cell > 0) { host->indly_sel = __be32_to_cpu(cell[0]); } else { host->indly_sel = -1; } if (host->id == SDIO_HOST_ID_3) { sprintf(path,"/top@%x/sdio3",IOADDR_TOP_REG_BASE); nodeoffset = fdt_path_offset((const void*)fdt_addr, path); cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "pinmux", NULL); host->pinmux_value = __be32_to_cpu(cell[0]); if (host->pinmux_value & PIN_SDIO_CFG_8BITS) host->enable_8bits = 1; else host->enable_8bits = 0; } #ifdef CONFIG_TARGET_NA51089 if (host->id == SDIO_HOST_ID_2) { sprintf(path,"/top@%x/sdio2",IOADDR_TOP_REG_BASE); nodeoffset = fdt_path_offset((const void*)fdt_addr, path); cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "pinmux", NULL); host->pinmux_value = __be32_to_cpu(cell[0]); } #endif } //============================================================================================================= /* Get SDIO controller register. @param[in] host host data structure @param[in] offset register offset in SDIO controller (word alignment) @return register value */ static REGVALUE sdiohost_getreg(struct mmc_nvt_host *host, u32 offset) { return readl(host->base + offset); } /* Set SDIO controller register. @param[in] host host data structure @param[in] offset offset in SDIO controller (word alignment) @param[in] value register value @return void */ static void sdiohost_setreg(struct mmc_nvt_host *host, u32 offset, REGVALUE value) { writel(value, host->base + offset); } #define RESET_TIMEOUT 10000 static void sdiohost_setphyrst(struct mmc_nvt_host *host) { union SDIO_PHY_REG phyreg, phyreg_read; u8 i; phyreg.reg = sdiohost_getreg(host, SDIO_PHY_REG_OFS); phyreg.bit.PHY_SW_RST = 1; sdiohost_setreg(host, SDIO_PHY_REG_OFS, phyreg.reg); while (1) { phyreg_read.reg = sdiohost_getreg(host, SDIO_PHY_REG_OFS); if ((phyreg_read.bit.PHY_SW_RST == 0) || (i == RESET_TIMEOUT)) break; i++; } if (i == RESET_TIMEOUT) printf("phy reset timeout\n"); } static void sdiohost_setphysample(struct mmc_nvt_host *host, BOOL internal, BOOL before) { union SDIO_DLY0_REG dly0_reg; dly0_reg.reg = sdiohost_getreg(host, SDIO_DLY0_REG_OFS); dly0_reg.bit.SRC_CLK_SEL = internal; // 0: from pad, 1: from internal dly0_reg.bit.PAD_CLK_SEL = before; // 0: after pad, 1: before pad sdiohost_setreg(host, SDIO_DLY0_REG_OFS, dly0_reg.reg); } #if 0 static void sdiohost_setphyclkoutdly(struct mmc_nvt_host *host, u32 dly_setting) { union SDIO_CLOCK_CTRL2_REG clk_crtl_reg; clk_crtl_reg.reg = sdiohost_getreg(host, SDIO_CLOCK_CTRL2_REG_OFS); clk_crtl_reg.bit.OUTDLY_SEL = dly_setting; sdiohost_setreg(host, SDIO_CLOCK_CTRL2_REG_OFS, clk_crtl_reg.reg); } #endif static void sdiohost_setphyclkindly(struct mmc_nvt_host *host, u32 dly_setting) { union SDIO_CLOCK_CTRL2_REG clk_crtl_reg; clk_crtl_reg.reg = sdiohost_getreg(host, SDIO_CLOCK_CTRL2_REG_OFS); clk_crtl_reg.bit.INDLY_SEL = dly_setting; sdiohost_setreg(host, SDIO_CLOCK_CTRL2_REG_OFS, clk_crtl_reg.reg); } /* Set SDIO bus width. @param[in] host host data structure @param[in] Width SDIO controller bus width - @b SDIO_BUS_WIDTH1: 1 bit data bus - @b SDIO_BUS_WIDTH4: 4 bits data bus - @b SDIO_BUS_WIDTH8: 8 bits data bus @return void */ void sdiohost_setbuswidth(struct mmc_nvt_host *host, u32 width) { union SDIO_BUS_WIDTH_REG widthreg; widthreg.reg = 0; widthreg.bit.BUS_WIDTH = width; sdiohost_setreg(host, SDIO_BUS_WIDTH_REG_OFS, widthreg.reg); } /* Set SDIO clock enable or disable When set to TRUE, SD controller will output SD clock to SD card. When set to FALSE, SD controller will not output SD clock to SD card. @param[in] host data structure @param[in] enableflag enable clock output - @b TRUE: enable SD clock output - @b FALSE: disable SD clock output @return void */ void sdiohost_enclkout(struct mmc_nvt_host *host, BOOL enableflag) { union SDIO_CLOCK_CTRL_REG clkctrlreg; clkctrlreg.reg = sdiohost_getreg(host, SDIO_CLOCK_CTRL_REG_OFS); if (enableflag == TRUE) { /* enabke SDIO CLK */ clkctrlreg.bit.CLK_DIS = 0; } else { /* disable SDIO CLK */ clkctrlreg.bit.CLK_DIS = 1; } sdiohost_setreg(host, SDIO_CLOCK_CTRL_REG_OFS, clkctrlreg.reg); } /* Set SDIO CLK card type. @param[in] host data structure @param[in] brisingsample SDIO controller input sampling timing - @b TRUE: sample at rising edge - @b FALSE :sample at falling edge @return void */ void sdiohost_setclktype(struct mmc_nvt_host *host, BOOL brisingsample) { union SDIO_CLOCK_CTRL_REG clkctrlreg; clkctrlreg.reg = sdiohost_getreg(host, SDIO_CLOCK_CTRL_REG_OFS); if (brisingsample) clkctrlreg.bit.CLK_SD = 1; else clkctrlreg.bit.CLK_SD = 0; sdiohost_setreg(host, SDIO_CLOCK_CTRL_REG_OFS, clkctrlreg.reg); } /* Set SDIO CLK cmd type. @param[in] host data sturcture @param[in] brisingsample SDIO controller input sampling timing - @b TRUE: sample at rising edge - @b FALSE :sample at falling edge @return void */ void sdiohost_setclkcmdtype(struct mmc_nvt_host *host, BOOL brisingsample) { union SDIO_CLOCK_CTRL_REG clkctrlreg; clkctrlreg.reg = sdiohost_getreg(host, SDIO_CLOCK_CTRL_REG_OFS); if (brisingsample) clkctrlreg.bit.CLK_SD_CMD = 1; else clkctrlreg.bit.CLK_SD_CMD = 0; sdiohost_setreg(host, SDIO_CLOCK_CTRL_REG_OFS, clkctrlreg.reg); } /** Get module clock rate Get module clock rate, one module at a time. @param[in] num Module ID(PLL_CLKSEL_*), one module at a time. Please refer to pll.h @return Moudle clock rate(PLL_CLKSEL_*_*), please refer to pll.h */ u32 pll_get_sdioclock_rate(int id) { REGVALUE regdata; if (id != SDIO_HOST_ID_3) HAL_READ_UINT32(PLL_SYS_CR_REG_OFS, regdata); else HAL_READ_UINT32(PLL_SYS_CR_REG_OFS + 0x4, regdata); if (id != SDIO_HOST_ID_2) regdata &= SDIO_MASK; else regdata &= SDIO2_MASK; return (u32)regdata; } /** Set module clock rate Set module clock rate, one module at a time. @param[in] id SDIO channel @param[in] num Module ID(PLL_CLKSEL_*), one module at a time. Please refer to pll.h @param[in] value Moudle clock rate(PLL_CLKSEL_*_*), please refer to pll.h @return void */ void pll_set_sdioclock_rate(int id, u32 value) { REGVALUE regdata; if (id != SDIO_HOST_ID_3) HAL_READ_UINT32(PLL_SYS_CR_REG_OFS, regdata); else HAL_READ_UINT32(PLL_SYS_CR_REG_OFS + 0x4, regdata); if (id != SDIO_HOST_ID_2) { regdata &= ~SDIO_MASK; regdata |= value; } else { regdata &= ~SDIO2_MASK; regdata |= value << 16;//Improve the read and write speed of the emmc added by greg } if (id != SDIO_HOST_ID_3) HAL_WRITE_UINT32(PLL_SYS_CR_REG_OFS, regdata); else HAL_WRITE_UINT32(PLL_SYS_CR_REG_OFS + 0x4, regdata); } /* Set bus clock. @param[in] id SDIO channel @param[in] uiclock SD bus clock in Hz @return void */ void nvt_clk_set_rate(int id, u32 uiclock) { u32 divider, src_clk = FPGA_SDIO_SRCCLK; divider = (src_clk + uiclock-1)/uiclock; if (!divider) divider = 1; pll_set_sdioclock_rate(id, divider-1); } /* Set SDIO bus clock. @param[in] host data structure @param[in] uiclock SD bus clock in Hz @return void */ void sdiohost_setbusclk(struct mmc_nvt_host *host, u32 uiclock, u32 *ns) { union SDIO_CLOCK_CTRL_REG clkctrlreg; /* Disable SDIO clk */ sdiohost_enclkout(host, FALSE); if (uiclock == 0) { return; } nvt_clk_set_rate(host->id, uiclock); if (ns) *ns = (1000000) / (uiclock/1000); /* Enable SDIO clk */ sdiohost_enclkout(host, TRUE); clkctrlreg.reg = sdiohost_getreg(host, SDIO_CLOCK_CTRL_REG_OFS); clkctrlreg.bit.DLY_ACT = 1; sdiohost_setreg(host, SDIO_CLOCK_CTRL_REG_OFS, clkctrlreg.reg); sdiohost_setclktype(host, TRUE); sdiohost_setclkcmdtype(host, TRUE); } /* Get SDIO bus clock. @return unit of Hz */ static u32 sdiohost_getbusclk(int id) { u32 uisourceclock; u32 uiclockdivider; uisourceclock = FPGA_SDIO_SRCCLK; uiclockdivider = pll_get_sdioclock_rate(id); return uisourceclock / (uiclockdivider + 1); } /* Get SDIO Busy or not @return TRUE: ready FALSE: busy */ BOOL sdiohost_getrdy(struct mmc_nvt_host *host) { union SDIO_BUS_STATUS_REG stsreg; stsreg.reg = sdiohost_getreg(host, SDIO_BUS_STATUS_REG_OFS); return stsreg.bit.CARD_READY; } /* Reset SDIO host controller. @return void */ void sdiohost_reset(struct mmc_nvt_host *host) { union SDIO_CMD_REG cmdreg; cmdreg.reg = sdiohost_getreg(host, SDIO_CMD_REG_OFS); cmdreg.bit.SDC_RST = 1; sdiohost_setreg(host, SDIO_CMD_REG_OFS, cmdreg.reg); while (1) { cmdreg.reg = sdiohost_getreg(host, SDIO_CMD_REG_OFS); if (cmdreg.bit.SDC_RST == 0) break; } if (host->mmc_input_clk > 1000000) udelay(1); else udelay((1000000/ host->mmc_input_clk) + 1); } /* Reset SDIO controller data state machine. @return void */ void sdiohost_resetdata(struct mmc_nvt_host *host) { union SDIO_DATA_CTRL_REG datactrlreg; union SDIO_FIFO_CONTROL_REG fifoctrlreg; union SDIO_DLY1_REG dlyreg; union SDIO_PHY_REG phyreg; union SDIO_PHY_REG phyreg_read; union SDIO_FIFO_SWITCH_REG fifoswitch; /* //#NT#Fix SDIO data state machine abnormal when DATA CRC/Timeout occurs before FIFO count complete */ union SDIO_STATUS_REG stsreg; /* SDIO bug: force to clear data end status to exit waiting data end state*/ stsreg.reg = 0; stsreg.bit.DATA_END = 1; sdiohost_setreg(host, SDIO_STATUS_REG_OFS, stsreg.reg); fifoctrlreg.reg = 0; sdiohost_setreg(host, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg); while (1) { fifoctrlreg.reg = sdiohost_getreg(host, SDIO_FIFO_CONTROL_REG_OFS); if (fifoctrlreg.bit.FIFO_EN == 0) break; } datactrlreg.reg = sdiohost_getreg(host, SDIO_DATA_CTRL_REG_OFS); datactrlreg.bit.DATA_EN = 0; sdiohost_setreg(host, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg); /* Fix SDIO data state machine abnormal when DATA CRC/Timeout occurs before FIFO count complete */ /* Do software reset to reset SD state machine */ sdiohost_reset(host); /* patch begin for sd write hang-up or write byte access error */ fifoswitch.reg = sdiohost_getreg(host, SDIO_FIFO_SWITCH_REG_OFS); fifoswitch.bit.FIFO_SWITCH_DLY = 1; sdiohost_setreg(host, SDIO_FIFO_SWITCH_REG_OFS, fifoswitch.reg); /* patch end for sd write hang-up or write byte access error */ phyreg.reg = sdiohost_getreg(host, SDIO_PHY_REG_OFS); phyreg.bit.PHY_SW_RST = 1; phyreg.bit.BLK_FIFO_EN = 1; sdiohost_setreg(host, SDIO_PHY_REG_OFS, phyreg.reg); while (1) { phyreg_read.reg = sdiohost_getreg(host, SDIO_PHY_REG_OFS); if (phyreg_read.bit.PHY_SW_RST == 0) break; } dlyreg.reg = sdiohost_getreg(host, SDIO_DLY1_REG_OFS); dlyreg.bit.DATA_READ_DLY = 2; dlyreg.bit.DET_READ_DLY = 2; sdiohost_setreg(host, SDIO_DLY1_REG_OFS, dlyreg.reg); } /* Set SDIO controller data timeout. @param[in] timeout time out value between data blocks (unit: SD clock) @return void */ void sdiohost_setdatatimeout(struct mmc_nvt_host *host, u32 timeout) { union SDIO_DATA_TIMER_REG timerreg; timerreg.bit.Timeout = timeout; sdiohost_setreg(host, SDIO_DATA_TIMER_REG_OFS, timerreg.reg); } /* Set PAD driving Sink @param[in] name driving sink name @param[in] paddriving driving current @return - @b E_OK: sucess - @b Else: fail */ static int pad_setdrivingsink(u32 name, u32 paddriving) { unsigned long padreg; unsigned long dwoffset = 0x0, bitoffset = 0x0, bitmask = 0x0; unsigned long driving = paddriving; if (name & PAD_DS_GROUP_40) { dwoffset = (PAD_DS10_REG_OFS - PAD_DS_REG_OFS); dwoffset += (((name & PAD_DS_GPIO_BASE_MASK)>>5)<<2); bitoffset = name & 0x1F; bitmask = 0x07; driving &= PAD_DS_GROUP_40_MSK; if (paddriving & PAD_DRIVINGSINK_5MA) driving = 0; else if (paddriving & PAD_DRIVINGSINK_10MA) driving = 1; else if (paddriving & PAD_DRIVINGSINK_15MA) driving = 2; else if (paddriving & PAD_DRIVINGSINK_20MA) driving = 3; else if (paddriving & PAD_DRIVINGSINK_25MA) driving = 4; else if (paddriving & PAD_DRIVINGSINK_30MA) driving = 5; else if (paddriving & PAD_DRIVINGSINK_35MA) driving = 6; else if (paddriving & PAD_DRIVINGSINK_40MA) driving = 7; else { printf("No Valid DS value.(0x%X), PAD_DS_GROUP_40\r\n", name); return E_PAR; } } else if (name & PAD_DS_GROUP_16) { #ifdef CONFIG_TARGET_NA51089 if ((name & ~PAD_DS_GROUP_MASK) >= (PAD_DS_HSIGPIO0 & ~PAD_DS_GROUP_MASK)) { dwoffset = (((name & PAD_DS_GPIO_BASE_MASK)>>5)<<2) + 0x28; } else { dwoffset = (((name & PAD_DS_GPIO_BASE_MASK)>>5)<<2); } bitoffset = name & 0x1F; bitmask = 0x03; if (name & PAD_DS_GROUP_16) { driving &= PAD_DS_GROUP_COMBO_MSK; if (driving == PAD_DRIVINGSINK_4MA) { driving = 0; } else if (driving == PAD_DRIVINGSINK_8MA) { driving = 1; } else if (driving == PAD_DRIVINGSINK_12MA) { driving = 2; } else if (driving == PAD_DRIVINGSINK_16MA) { driving = 3; } else { printf("No Valid DS value.(0x%X), PAD_DS_GROUP_16\r\n", name); return E_PAR; } } else { if (driving == PAD_DRIVINGSINK_4MA) { driving = 0; } else if (driving == PAD_DRIVINGSINK_10MA) { driving = 1; } else { printf("No Valid DS value.(0x%X), PAD_DS_GROUP_10\r\n", name); return E_PAR; } } #else dwoffset = (((name & PAD_DS_GPIO_BASE_MASK)>>5)<<2); bitoffset = name & 0x1F; bitmask = 0x01; driving &= PAD_DS_GROUP_16_MSK; if (driving & PAD_DRIVINGSINK_6MA) { driving = 0; } else if (driving & PAD_DRIVINGSINK_16MA) { driving = 1; } else { printf("No Valid DS value.(0x%X), PAD_DS_GROUP_16/PAD_DS_GROUP_10\r\n", name); return E_PAR; } #endif } else { /* 10MA GROUP */ if (name >= PAD_DS_HSIGPIO0) { dwoffset = (((name & PAD_DS_GPIO_BASE_MASK)>>5)<<2) + 0x28; } else { dwoffset = (((name & PAD_DS_GPIO_BASE_MASK)>>5)<<2); } bitoffset = name & 0x1F; bitmask = 0x01; driving &= PAD_DS_GROUP_10_MSK; if (driving & PAD_DRIVINGSINK_4MA) { driving = 0; } else if (driving & PAD_DRIVINGSINK_10MA) { driving = 1; } else { return E_PAR; } } HAL_READ_UINT32(IOADDR_PAD_REG_BASE + PAD_DS_REG_OFS + dwoffset, padreg); padreg &= ~(bitmask << bitoffset); padreg |= (driving << bitoffset); HAL_WRITE_UINT32(IOADDR_PAD_REG_BASE + PAD_DS_REG_OFS + dwoffset, padreg); return E_OK; } static u32 driving_xfer(u32 driving) { u32 pad_driving; if (driving == 40) pad_driving = PAD_DRIVINGSINK_40MA; else if (driving == 10) pad_driving = PAD_DRIVINGSINK_10MA; else if (driving == 15) pad_driving = PAD_DRIVINGSINK_15MA; else if (driving == 20) pad_driving = PAD_DRIVINGSINK_20MA; else if (driving == 25) pad_driving = PAD_DRIVINGSINK_25MA; else if (driving == 30) pad_driving = PAD_DRIVINGSINK_30MA; else if (driving == 35) pad_driving = PAD_DRIVINGSINK_35MA; else if (driving == 16) pad_driving = PAD_DRIVINGSINK_16MA; else if (driving == 6) pad_driving = PAD_DRIVINGSINK_6MA; else if (driving == 4) pad_driving = PAD_DRIVINGSINK_4MA; else if (driving == 8) pad_driving = PAD_DRIVINGSINK_8MA; else if (driving == 12) pad_driving = PAD_DRIVINGSINK_12MA; else if (driving == 16) pad_driving = PAD_DRIVINGSINK_16MA; else pad_driving = PAD_DRIVINGSINK_5MA; return pad_driving; } /* Set PAD drive/sink of clock pin for specified SDIO channel. @param[in] id SDIO channel ID - @b SDIO_HOST_ID_1: SDIO1 - @b SDIO_HOST_ID_2: SDIO2 @param[in] driving desired driving value * 10, unit: mA valid value: 50 ~ 200 @return - @b E_OK: sucess - @b Else: fail */ static int sdiohost_setpaddriving(struct mmc_nvt_host *host, SDIO_SPEED_MODE mode) { UINT32 data_uidriving, cmd_uidriving, clk_uidriving; #ifdef CONFIG_NVT_FPGA_EMULATION return 0; #endif if (mode == SDIO_MODE_DS) { data_uidriving = driving_xfer(host->pad_driving[SDIO_DS_MODE_DATA]); cmd_uidriving = driving_xfer(host->pad_driving[SDIO_DS_MODE_CMD]); clk_uidriving = driving_xfer(host->pad_driving[SDIO_DS_MODE_CLK]); } else if (mode == SDIO_MODE_HS) { data_uidriving = driving_xfer(host->pad_driving[SDIO_HS_MODE_DATA]); cmd_uidriving = driving_xfer(host->pad_driving[SDIO_HS_MODE_CMD]); clk_uidriving = driving_xfer(host->pad_driving[SDIO_HS_MODE_CLK]); } else if (mode == SDIO_MODE_SDR50) { data_uidriving = driving_xfer(host->pad_driving[SDIO_SDR50_MODE_DATA]); cmd_uidriving = driving_xfer(host->pad_driving[SDIO_SDR50_MODE_CMD]); clk_uidriving = driving_xfer(host->pad_driving[SDIO_SDR50_MODE_CLK]); } else { data_uidriving = driving_xfer(host->pad_driving[SDIO_SDR104_MODE_DATA]); cmd_uidriving = driving_xfer(host->pad_driving[SDIO_SDR104_MODE_CMD]); clk_uidriving = driving_xfer(host->pad_driving[SDIO_SDR104_MODE_CLK]); } if (host->id == SDIO_HOST_ID_1) { pad_setdrivingsink(PAD_DS_CGPIO12, cmd_uidriving); pad_setdrivingsink(PAD_DS_CGPIO13, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO14, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO15, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO16, data_uidriving); return pad_setdrivingsink(PAD_DS_CGPIO11, clk_uidriving); } else if (host->id == SDIO_HOST_ID_2) { pad_setdrivingsink(PAD_DS_CGPIO18, cmd_uidriving); pad_setdrivingsink(PAD_DS_CGPIO19, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO20, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO21, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO22, data_uidriving); return pad_setdrivingsink(PAD_DS_CGPIO17, clk_uidriving); } else { if (host->enable_8bits) { pad_setdrivingsink(PAD_DS_CGPIO4, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO5, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO6, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO7, data_uidriving); } pad_setdrivingsink(PAD_DS_CGPIO0, cmd_uidriving); pad_setdrivingsink(PAD_DS_CGPIO1, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO2, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO3, data_uidriving); pad_setdrivingsink(PAD_DS_CGPIO9, data_uidriving); return pad_setdrivingsink(PAD_DS_CGPIO8, clk_uidriving); } return 0; } /* Delay for SDIO module @param[in] uid the count for dummy read @return void */ void sdiohost_delayd(struct mmc_nvt_host *host, u32 uid) { u32 i; for (i = uid; i; i--) sdiohost_getreg(host, SDIO_CMD_REG_OFS); } /* Set SDIO controller block size. @param[in] host data structure @return void */ void sdiohost_setblksize(struct mmc_nvt_host *host) { union SDIO_DATA_CTRL_REG datactrlreg; datactrlreg.reg = sdiohost_getreg(host, SDIO_DATA_CTRL_REG_OFS); datactrlreg.bit.BLK_SIZE = host->data->blocksize; sdiohost_setreg(host, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg); } /* Setup SDIO controller data transfer in DMA mode. @param[in] uidmaaddress buffer DRAM address - possible value: 0x000_0000 ~ 0xFFF_FFFF @param[in] uidatalength total transfer length - possible value: 0x000_0001 ~ 0x3FF_FFFF @param[in] bisread read/write mode - @b TRUE: indicate data read transfer - @b FALSE: indicate data write transfer @return void */ void sdiohost_setupdatatransferdma(struct mmc_nvt_host *host , u32 uidmaaddress, u32 uidatalength, BOOL bisread) { union SDIO_DATA_CTRL_REG datactrlreg; union SDIO_DATA_LENGTH_REG datalenreg; union SDIO_FIFO_CONTROL_REG fifoctrlreg; union SDIO_DMA_START_ADDR_REG dmaaddrreg; u32 uibusclk; /* dummy read for patch */ sdiohost_delayd(host, 2); uibusclk = sdiohost_getbusclk(host->id); if (uibusclk >= 48000000) sdiohost_delayd(host, 3); else if ((uibusclk >= 24000000) && (uibusclk < 48000000)) sdiohost_delayd(host, 6); else if ((uibusclk >= 12000000) && (uibusclk < 24000000)) sdiohost_delayd(host, 9); else sdiohost_delayd(host, 21); /* patch for sd fifo bug end */ datactrlreg.reg = sdiohost_getreg(host, SDIO_DATA_CTRL_REG_OFS); /* multiple read => disable SDIO INT detection after transfer end */ if (bisread && (uidatalength > datactrlreg.bit.BLK_SIZE)) datactrlreg.bit.DIS_SDIO_INT_PERIOD = 1; else datactrlreg.bit.DIS_SDIO_INT_PERIOD = 0; /*move data en after fifo en*/ /*datactrlreg.bit.DATA_EN = 1;*/ sdiohost_setreg(host, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg); dmaaddrreg.reg = 0; dmaaddrreg.bit.DRAM_ADDR = uidmaaddress; sdiohost_setreg(host, SDIO_DMA_START_ADDR_REG_OFS, dmaaddrreg.reg); datalenreg.reg = 0; datalenreg.bit.LENGTH = uidatalength; sdiohost_setreg(host, SDIO_DATA_LENGTH_REG_OFS, datalenreg.reg); fifoctrlreg.reg = 0; /* Flush cache in DMA mode*/ if (!bisread) fifoctrlreg.bit.FIFO_DIR = 1; else fifoctrlreg.bit.FIFO_DIR = 0; fifoctrlreg.bit.FIFO_MODE = 1; sdiohost_setreg(host, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg); datactrlreg.reg = sdiohost_getreg(host, SDIO_DATA_CTRL_REG_OFS); datactrlreg.bit.DATA_EN = 1; sdiohost_setreg(host, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg); fifoctrlreg.bit.FIFO_EN = 1; sdiohost_setreg(host, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg); } /* Setup SDIO controller data transfer in PIO mode. @param[in] uidatalength total transfer length - possible value: 0x000_0001 ~ 0x3FF_FFFF @param[in] bisread read/write mode - @b TRUE: indicate data read transfer - @b FALSE: indicate data write transfer @return void */ void sdiohost_setupdatatransferpio(struct mmc_nvt_host *host, u32 uidatalength, BOOL bisread) { union SDIO_DATA_CTRL_REG datactrlreg; union SDIO_DATA_LENGTH_REG datalenreg; union SDIO_FIFO_CONTROL_REG fifoctrlreg; u32 uibusclk; /* dummy read for patch */ sdiohost_delayd(host, 2); uibusclk = sdiohost_getbusclk(host->id); if (uibusclk >= 48000000) sdiohost_delayd(host, 3); else if ((uibusclk >= 24000000) && (uibusclk < 48000000)) sdiohost_delayd(host, 6); else if ((uibusclk >= 12000000) && (uibusclk < 24000000)) sdiohost_delayd(host, 9); else sdiohost_delayd(host, 21); /* patch for sd fifo bug end */ datactrlreg.reg = sdiohost_getreg(host, SDIO_DATA_CTRL_REG_OFS); /* multiple read => disable SDIO INT detection after transfer end */ if (bisread && (uidatalength > datactrlreg.bit.BLK_SIZE)) datactrlreg.bit.DIS_SDIO_INT_PERIOD = 1; else datactrlreg.bit.DIS_SDIO_INT_PERIOD = 0; datactrlreg.bit.DATA_EN = 1; sdiohost_setreg(host, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg); datalenreg.reg = 0; datalenreg.bit.LENGTH = uidatalength; sdiohost_setreg(host, SDIO_DATA_LENGTH_REG_OFS, datalenreg.reg); fifoctrlreg.reg = 0; if (!bisread) fifoctrlreg.bit.FIFO_DIR = 1; else fifoctrlreg.bit.FIFO_DIR = 0; sdiohost_setreg(host, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg); fifoctrlreg.bit.FIFO_EN = 1; sdiohost_setreg(host, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg); } /* Write SDIO data blocks. @note This function should only be called in PIO mode. @param[in] pbuf buffer DRAM address @param[in] uiLength total length (block alignment) @return - @b E_OK: success - @b E_SYS: data CRC or data timeout error */ ER sdiohost_writeblock(struct mmc_nvt_host *host, u8 *pbuf, u32 uilength) { u32 uiwordcount, i, *pbufword; u32 uifullcount, uiremaincount; BOOL bwordalignment; union SDIO_DATA_PORT_REG datareg; union SDIO_FIFO_STATUS_REG fifostsreg; union SDIO_STATUS_REG stsreg; uiwordcount = (uilength + sizeof(u32) - 1) / sizeof(u32); uifullcount = uiwordcount / SDIO_HOST_DATA_FIFO_DEPTH; uiremaincount = uiwordcount % SDIO_HOST_DATA_FIFO_DEPTH; pbufword = (u32 *)pbuf; if ((u32)pbuf & 0x3) bwordalignment = FALSE; else bwordalignment = TRUE; while (uifullcount) { fifostsreg.reg = sdiohost_getreg(host, SDIO_FIFO_STATUS_REG_OFS); if (fifostsreg.bit.FIFO_EMPTY) { if (bwordalignment == TRUE) { /* Word alignment*/ for (i = SDIO_HOST_DATA_FIFO_DEPTH; i; i--) { sdiohost_setreg(host, SDIO_DATA_PORT_REG_OFS, \ *pbufword++); } } else { /* Not word alignment*/ for (i = SDIO_HOST_DATA_FIFO_DEPTH; i; i--) { datareg.reg = *pbuf++; datareg.reg |= (*pbuf++) << 8; datareg.reg |= (*pbuf++) << 16; datareg.reg |= (*pbuf++) << 24; sdiohost_setreg(host, SDIO_DATA_PORT_REG_OFS, \ datareg.reg); } } uifullcount--; } stsreg.reg = sdiohost_getreg(host, SDIO_STATUS_REG_OFS); if (stsreg.bit.DATA_CRC_FAIL || stsreg.bit.DATA_TIMEOUT) { printf("write block fail\n"); return E_SYS; } } if (uiremaincount) { while (1) { fifostsreg.reg = \ sdiohost_getreg(host, SDIO_FIFO_STATUS_REG_OFS); if (fifostsreg.bit.FIFO_EMPTY) break; stsreg.reg = sdiohost_getreg(host, SDIO_STATUS_REG_OFS); if (stsreg.bit.DATA_CRC_FAIL || stsreg.bit.DATA_TIMEOUT) return E_SYS; } if (bwordalignment == TRUE) { /* Word alignment*/ for (i = uiremaincount; i; i--) { sdiohost_setreg(host, SDIO_DATA_PORT_REG_OFS, \ *pbufword++); } } else { /* Not word alignment*/ for (i = uiremaincount; i; i--) { datareg.reg = *pbuf++; datareg.reg |= (*pbuf++) << 8; datareg.reg |= (*pbuf++) << 16; datareg.reg |= (*pbuf++) << 24; sdiohost_setreg(host, SDIO_DATA_PORT_REG_OFS, \ datareg.reg); } } } return E_OK; } /* Read SDIO data blocks. @note This function should only be called in PIO mode. @param[out] pbuf buffer DRAM address @param[in] uiLength total length (block alignment) @return - @b E_OK: success - @b E_SYS: data CRC or data timeout error */ ER sdiohost_readblock(struct mmc_nvt_host *host, u8 *pbuf, u32 uilength) { u32 uiwordcount, i, *pbufword; u32 uifullcount, uiremaincount; BOOL bwordalignment; union SDIO_DATA_PORT_REG datareg; union SDIO_FIFO_STATUS_REG fifostsreg; union SDIO_STATUS_REG stsreg; uiwordcount = (uilength + sizeof(u32) - 1) / sizeof(u32); uifullcount = uiwordcount / SDIO_HOST_DATA_FIFO_DEPTH; uiremaincount = uiwordcount % SDIO_HOST_DATA_FIFO_DEPTH; pbufword = (u32 *)pbuf; if ((u32)pbuf & 0x3) bwordalignment = FALSE; else bwordalignment = TRUE; while (uifullcount) { fifostsreg.reg = sdiohost_getreg(host, SDIO_FIFO_STATUS_REG_OFS); if (fifostsreg.bit.FIFO_FULL) { if (bwordalignment == TRUE) { /* Word alignment*/ for (i = SDIO_HOST_DATA_FIFO_DEPTH; i; i--) { *pbufword++ = \ sdiohost_getreg(host, SDIO_DATA_PORT_REG_OFS); } } else { /* Not word alignment*/ for (i = SDIO_HOST_DATA_FIFO_DEPTH; i; i--) { datareg.reg = \ sdiohost_getreg(host, SDIO_DATA_PORT_REG_OFS); *pbuf++ = datareg.reg & 0xFF; *pbuf++ = (datareg.reg>>8) & 0xFF; *pbuf++ = (datareg.reg>>16) & 0xFF; *pbuf++ = (datareg.reg>>24) & 0xFF; } } uifullcount--; } stsreg.reg = sdiohost_getreg(host, SDIO_STATUS_REG_OFS); if (stsreg.bit.DATA_CRC_FAIL || stsreg.bit.DATA_TIMEOUT) return E_SYS; } if (uiremaincount) { while (1) { fifostsreg.reg = \ sdiohost_getreg(host, SDIO_FIFO_STATUS_REG_OFS); if (fifostsreg.bit.FIFO_CNT == uiremaincount) break; stsreg.reg = sdiohost_getreg(host, SDIO_STATUS_REG_OFS); if (stsreg.bit.DATA_CRC_FAIL || stsreg.bit.DATA_TIMEOUT) return E_SYS; } if (bwordalignment == TRUE) { /* Word alignment*/ for (i = uiremaincount; i; i--) { *pbufword++ = \ sdiohost_getreg(host, SDIO_DATA_PORT_REG_OFS); } } else { /* Not word alignment*/ for (i = uiremaincount; i; i--) { datareg.reg = \ sdiohost_getreg(host, SDIO_DATA_PORT_REG_OFS); *pbuf++ = datareg.reg & 0xFF; *pbuf++ = (datareg.reg>>8) & 0xFF; *pbuf++ = (datareg.reg>>16) & 0xFF; *pbuf++ = (datareg.reg>>24) & 0xFF; } } } return E_OK; } u32 sdiohost_getstatus(struct mmc_nvt_host *host) { return sdiohost_getreg(host, SDIO_STATUS_REG_OFS); } void sdiohost_setstatus(struct mmc_nvt_host *host, u32 status) { sdiohost_setreg(host, SDIO_STATUS_REG_OFS, status); } static void sdiohost_fifo_data_trans(struct mmc_nvt_host *host, unsigned int n) { if (host->data_dir == SDIO_HOST_WRITE_DATA) { host->buffer = (u8*) host->data->src; sdiohost_writeblock(host, (u8 *)host->buffer, \ host->buffer_bytes_left); host->bytes_left -= host->buffer_bytes_left; } else { host->buffer = (u8*) host->data->dest; sdiohost_readblock(host, (u8 *)host->buffer, \ host->buffer_bytes_left); host->bytes_left -= host->buffer_bytes_left; } } void sdiohost_clrfifoen(struct mmc_nvt_host *host) { union SDIO_FIFO_CONTROL_REG fifoctrlreg; fifoctrlreg.reg = sdiohost_getreg(host, SDIO_FIFO_CONTROL_REG_OFS); fifoctrlreg.bit.FIFO_EN = 0; sdiohost_setreg(host, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg); } u32 sdiohost_getfifodir(struct mmc_nvt_host *host) { union SDIO_FIFO_CONTROL_REG fifoctrlreg; fifoctrlreg.reg = sdiohost_getreg(host, SDIO_FIFO_CONTROL_REG_OFS); return fifoctrlreg.bit.FIFO_DIR; } void sdiohost_waitfifoempty(struct mmc_nvt_host *host) { union SDIO_FIFO_STATUS_REG fifostsreg; u32 read0, read1; read0 = 0; while (1) { fifostsreg.reg = sdiohost_getreg(host, SDIO_FIFO_STATUS_REG_OFS); read1 = fifostsreg.bit.FIFO_EMPTY; if (read0 & read1) break; else read0 = read1; } } void sdiohost_getlongrsp(struct mmc_nvt_host *host, u32 *prsp3, u32 *prsp2, \ u32 *prsp1, u32 *prsp0) { union SDIO_RSP0_REG rsp0reg; union SDIO_RSP1_REG rsp1reg; union SDIO_RSP2_REG rsp2reg; union SDIO_RSP3_REG rsp3reg; rsp0reg.reg = sdiohost_getreg(host, SDIO_RSP0_REG_OFS); *prsp0 = (u32) rsp0reg.reg; rsp1reg.reg = sdiohost_getreg(host, SDIO_RSP1_REG_OFS); *prsp1 = (u32) rsp1reg.reg; rsp2reg.reg = sdiohost_getreg(host, SDIO_RSP2_REG_OFS); *prsp2 = (u32) rsp2reg.reg; rsp3reg.reg = sdiohost_getreg(host, SDIO_RSP3_REG_OFS); *prsp3 = (u32) rsp3reg.reg; } void sdiohost_getshortrsp(struct mmc_nvt_host *host, u32 *prsp) { union SDIO_RSP0_REG rspreg; rspreg.reg = sdiohost_getreg(host, SDIO_RSP0_REG_OFS); *prsp = (u32) rspreg.reg; } static void mmc_nvt_cmd_done(struct mmc_nvt_host *host) { struct mmc_cmd *cmd = host->cmd; host->cmd = NULL; if (cmd->resp_type & MMC_RSP_PRESENT) { if (cmd->resp_type & MMC_RSP_136) { /* response type 2 */ sdiohost_getlongrsp(host, \ (u32 *)&cmd->response[0], (u32 *)&cmd->response[1], (u32 *)&cmd->response[2], (u32 *)&cmd->response[3]); debug("lrsp 0x%x 0x%x 0x%x 0x%x\n", cmd->response[0], cmd->response[1], cmd->response[2], cmd->response[3]); } else { /* response types 1, 1b, 3, 4, 5, 6 */ sdiohost_getshortrsp(host, (u32 *)&cmd->response[0]); debug("rsp 0x%x\n", cmd->response[0]); } } } static ER sdiohost_transfer(struct mmc_nvt_host *host) { u32 status, qstatus = 0; int end_command = 0; int end_transfer = 0; int err_sts = 1, timeout = 0; struct mmc_data *data = host->data; while(1) { status = sdiohost_getstatus(host); //printf("cmd status is 0x%x\n", status); if (status & SDIO_STATUS_REG_CMD_SEND) { qstatus = status; sdiohost_setstatus(host, SDIO_STATUS_REG_CMD_SEND); break; } } if (data && host->do_dma) { while(1) { status = sdiohost_getstatus(host); //printf("data status is 0x%x\n", status); if ((status & SDIO_STATUS_REG_DATA_END) || \ (status & SDIO_STATUS_REG_DATA_TIMEOUT)) { qstatus = status; sdiohost_setstatus(host, SDIO_STATUS_REG_DATA_END); break; } else if ((status & SDIO_STATUS_REG_RSP_CRC_FAIL) || \ (status & SDIO_STATUS_REG_DATA_CRC_FAIL)) { qstatus = status; //printf("crc status is 0x%x\n", status); break; } } } if ((qstatus & SDIO_STATUS_REG_RSP_CRC_FAIL) || (qstatus & SDIO_STATUS_REG_DATA_CRC_FAIL) || (qstatus & SDIO_STATUS_REG_RSP_TIMEOUT) || (qstatus & SDIO_STATUS_REG_DATA_TIMEOUT)) { err_sts = 1; } else { err_sts = 0; } //printf("qstatus is 0x%x, err_sts %d\n", qstatus, err_sts); if (qstatus & MMCST_RSP_TIMEOUT) { /* Command timeout */ if (host->cmd) { printf("CMD%d timeout, status %x\n",host->cmd->cmdidx, qstatus); if (host->data) sdiohost_resetdata(host); } //printf("MMCST_RSP_TIMEOUT\r\n"); sdiohost_setstatus(host, MMCST_RSP_TIMEOUT); timeout = 1; } if (qstatus & MMCST_DATA_TIMEOUT) { printf("data timeout (CMD%d), status 0x%x\n", host->cmd->cmdidx, qstatus); if (host->data) sdiohost_resetdata(host); //printf("MMCST_DATA_TIMEOUT\r\n"); sdiohost_setstatus(host, MMCST_DATA_TIMEOUT); } if (qstatus & MMCST_RSP_CRC_FAIL) { /* Command CRC error */ printf("Command CRC error\n"); //printf("MMCST_RSP_CRC_FAIL\r\n"); sdiohost_setstatus(host, MMCST_RSP_CRC_FAIL); } if (qstatus & MMCST_DATA_CRC_FAIL) { /* Data CRC error */ printf("data %s error\n", \ (host->data->flags & MMC_DATA_WRITE) ? "write" : "read"); printf("(CMD%d), status 0x%x\n", host->cmd->cmdidx, qstatus); sdiohost_resetdata(host); //printf("MMCST_DATA_CRC_FAIL\r\n"); sdiohost_setstatus(host, MMCST_DATA_CRC_FAIL); } if (((qstatus & MMCST_RSP_CRC_OK) || (qstatus & MMCST_CMD_SENT)) && !timeout) { /* End of command phase */ if (data == NULL) end_command = (int) host->cmd; else { if ((host->bytes_left > 0) && (host->do_dma == 0)) { /* if datasize < rw_threshold * no RX ints are generated */ sdiohost_fifo_data_trans(host, host->bytes_left); } } if (qstatus & MMCST_RSP_CRC_OK) sdiohost_setstatus(host, MMCST_RSP_CRC_OK); if (qstatus & MMCST_CMD_SENT) sdiohost_setstatus(host, MMCST_CMD_SENT); } if ((qstatus & MMCST_RSP_CRC_OK)) end_command = (int) host->cmd; if (data && !host->do_dma && !timeout) { while(1) { status = sdiohost_getstatus(host); if ((status & SDIO_STATUS_REG_DATA_END) || \ (status & SDIO_STATUS_REG_DATA_TIMEOUT)) { qstatus = status; sdiohost_setstatus(host, SDIO_STATUS_REG_DATA_END); break; } else if ((status & SDIO_STATUS_REG_RSP_CRC_FAIL) || (status & SDIO_STATUS_REG_RSP_TIMEOUT) || \ (status & SDIO_STATUS_REG_DATA_CRC_FAIL)) { qstatus = status; break; } } } if ((qstatus & MMCST_DATA_END) || (qstatus & MMCST_DATA_CRC_OK)) { end_transfer = 1; data->dest += data->blocksize; if (!sdiohost_getfifodir(host)) { if (qstatus & MMCST_DATA_END) { if ((qstatus & MMCST_DMA_ERROR) != MMCST_DMA_ERROR) { sdiohost_waitfifoempty(host); } sdiohost_clrfifoen(host); } } if (qstatus & MMCST_DATA_END) sdiohost_setstatus(host, MMCST_DATA_END); if (qstatus & MMCST_DATA_CRC_OK) sdiohost_setstatus(host, MMCST_DATA_CRC_OK); } if (end_command) mmc_nvt_cmd_done(host); if (end_transfer && (!host->cmd)) { host->data = NULL; host->cmd = NULL; } if (err_sts) { if (timeout) return -ETIMEDOUT; status = sdiohost_getstatus(host); if(status) printf("end status is 0x%x\n", status); return -ECOMM; } return SDIO_HOST_CMD_OK; } /* Send SD command to SD bus. @param[in] cmd command value @param[in] rsptype response type - @b SDIO_HOST_RSP_NONE: no response is required - @b SDIO_HOST_RSP_SHORT: need short (32 bits) response - @b SDIO_HOST_RSP_LONG: need long (128 bits) response @param[in] beniointdetect enable SDIO INT detect after command end - @b TRUE: enable SDIO INT detection - @b FALSE: keep SDIO INT detection @return command result - @b SDIO_HOST_CMD_OK: command execution success - @b SDIO_HOST_RSP_TIMEOUT: response timeout. no response got from card. - @b SDIO_HOST_RSP_CRCFAIL: response CRC fail. - @b SDIO_HOST_CMD_FAIL: other fail. */ ER sdiohost_sendcmd(struct mmc_nvt_host *host, \ u32 cmd, SDIO_HOST_RESPONSE rsptype, BOOL beniointdetect) { union SDIO_CMD_REG cmdreg; u32 status; /*cmdreg.reg = 0;*/ cmdreg.reg = sdiohost_getreg(host, SDIO_CMD_REG_OFS); cmdreg.bit.CMD_IDX = 0; cmdreg.bit.NEED_RSP = 0; cmdreg.bit.LONG_RSP = 0; cmdreg.bit.RSP_TIMEOUT_TYPE = 0; cmdreg.bit.ENABLE_SDIO_INT_DETECT = beniointdetect; if (rsptype != SDIO_HOST_RSP_NONE) { /* Need response */ cmdreg.bit.NEED_RSP = 1; switch (rsptype) { default: break; case SDIO_HOST_RSP_LONG: cmdreg.bit.LONG_RSP = 1; break; case SDIO_HOST_RSP_SHORT_TYPE2: cmdreg.bit.RSP_TIMEOUT_TYPE = 1; break; case SDIO_HOST_RSP_LONG_TYPE2: cmdreg.bit.RSP_TIMEOUT_TYPE = 1; cmdreg.bit.LONG_RSP = 1; break; } } cmdreg.bit.CMD_IDX = cmd; sdiohost_setreg(host, SDIO_CMD_REG_OFS, cmdreg.reg); /*Clear all status*/ status = sdiohost_getstatus(host); sdiohost_setstatus(host, status); /* Start command/data transmits */ cmdreg.bit.CMD_EN = 1; sdiohost_setreg(host, SDIO_CMD_REG_OFS, cmdreg.reg); return sdiohost_transfer(host); } ER sdiohost_sendsdcmd(struct mmc_nvt_host *host, u32 cmdpart) { BOOL benintdetect = FALSE; SDIO_HOST_RESPONSE rsptype = SDIO_HOST_RSP_NONE; u32 param = host->cmd->cmdarg; if ((cmdpart & SDIO_CMD_REG_LONG_RSP) == SDIO_CMD_REG_LONG_RSP) { if (cmdpart & SDIO_CMD_REG_RSP_TYPE2) rsptype = SDIO_HOST_RSP_LONG_TYPE2; else rsptype = SDIO_HOST_RSP_LONG; } else if (cmdpart & SDIO_CMD_REG_VOLTAGE_SWITCH_DETECT) { rsptype = SDIO_HOST_RSP_VOLT_DETECT; } else if (cmdpart & SDIO_CMD_REG_NEED_RSP) { if (cmdpart & SDIO_CMD_REG_RSP_TYPE2) rsptype = SDIO_HOST_RSP_SHORT_TYPE2; else rsptype = SDIO_HOST_RSP_SHORT; } if (cmdpart & SDIO_CMD_REG_ABORT) benintdetect = TRUE; sdiohost_setreg(host, SDIO_ARGU_REG_OFS, param); //printf("bEnIntDetect %d\r\n",benintdetect); return sdiohost_sendcmd(host, cmdpart & SDIO_CMD_REG_INDEX, \ rsptype, benintdetect); } static void mmc_nvt_prepare_data(struct mmc_nvt_host *host) { u32 size; if (host->data == NULL) { return; } sdiohost_setblksize(host); host->buffer = (u8*)(host->data->dest); host->bytes_left = host->data->blocks * host->data->blocksize; host->data_dir = ((host->data->flags & MMC_DATA_WRITE) ? SDIO_HOST_WRITE_DATA : SDIO_HOST_READ_DATA); #if (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64)) if ((host->bytes_left % ARCH_DMA_MINALIGN) || ((u32)host->buffer % ARCH_DMA_MINALIGN) || (host->mmc_input_clk < 24000000)) { if (host->mmc_input_clk < 24000000) { printf("clk(%dHz) < 24MHz, force to PIO\n", host->mmc_input_clk); } #else if ((host->bytes_left % ARCH_DMA_MINALIGN) || ((u32)host->buffer % ARCH_DMA_MINALIGN)) { #endif host->do_dma = 0; host->buffer_bytes_left = host->bytes_left; } else { host->do_dma = 1; } if (host->do_dma) { size = (u32)(host->buffer) + host->bytes_left; if (host->data_dir == SDIO_HOST_WRITE_DATA) flush_dcache_range(ALIGN_FLOOR((u32)host->buffer, ARCH_DMA_MINALIGN), (u32)roundup(size,ARCH_DMA_MINALIGN)); else invalidate_dcache_range(ALIGN_FLOOR((u32)host->buffer, ARCH_DMA_MINALIGN), (u32)roundup(size,ARCH_DMA_MINALIGN)); sdiohost_setupdatatransferdma(host, (u32)host->buffer, \ host->bytes_left, host->data_dir); /* zero this to ensure we take no PIO paths */ host->bytes_left = 0; } else { sdiohost_setupdatatransferpio(host, \ host->buffer_bytes_left, \ host->data_dir); } } int mmc_nvt_start_command(struct mmc_nvt_host *host) { u32 cmd_reg = 0; char *s; switch (host->cmd->resp_type & mmc_resp_type) { case MMC_RSP_R1: /* 48 bits, CRC */ s = ", R1"; cmd_reg |= SDIO_CMD_REG_NEED_RSP; break; case MMC_RSP_R1b: s = ", R1b"; /* There's some spec confusion about when R1B is * allowed, but if the card doesn't issue a BUSY * then it's harmless for us to allow it. */ /*need to check card busy CARD_BUSY2READY bit or *send _SDIO_SD_SEND_STATUS to check */ cmd_reg |= SDIO_CMD_REG_NEED_RSP; /* FALLTHROUGH */ break; case MMC_RSP_R2: /* 136 bits, CRC */ s = ", R2"; cmd_reg |= SDIO_CMD_REG_LONG_RSP; break; case MMC_RSP_R3: /* 48 bits, no CRC */ s = ", R3/R4"; cmd_reg |= SDIO_CMD_REG_NEED_RSP; break; default: s = ", Rx"; cmd_reg |= 0; break; }; debug("CMD%d, arg 0x%08x %s\n", host->cmd->cmdidx, host->cmd->cmdarg, s); /* Set command index */ cmd_reg |= host->cmd->cmdidx; return sdiohost_sendsdcmd(host, cmd_reg); } #if !CONFIG_IS_ENABLED(DM_MMC) static int host_request(struct mmc *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc_nvt_host *host = dev->priv; #else static int host_request(struct udevice *udev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc_nvt_host *host = dev_get_priv(udev); struct mmc *dev = mmc_get_mmc_dev(udev); #endif int result; unsigned int time_start, time_now, mmcst = 0; host->data = data; host->cmd = cmd; static int cur_clk = 0; #ifdef CONFIG_NVT_IVOT_EMMC if (data || (host->id == SDIO_HOST_ID_3)) { #else if (data) { #endif time_start = get_timer (0); while(1) { mmcst = sdiohost_getrdy(host); if (mmcst == true) break; #ifdef CONFIG_NVT_IVOT_EMMC if (host->id != SDIO_HOST_ID_3) { time_now = get_timer (0); if ((time_now - time_start) > 1000) break; } #else time_now = get_timer (0); if ((time_now - time_start) > 1000) break; #endif } if (mmcst == false) { printf("still busy\n"); return -ETIMEDOUT; } } if(dev->clock != cur_clk) { sdiohost_setdatatimeout(host, (dev->clock/1000)*300); } cur_clk = dev->clock; mmc_nvt_prepare_data(host); result = mmc_nvt_start_command(host); return result; } #if !CONFIG_IS_ENABLED(DM_MMC) /* MMC uses open drain drivers in the enumeration phase */ static int mmc_host_reset(struct mmc *dev) { return 0; } #endif static int nvt_emmc_arch_host_preinit(struct mmc_nvt_host *host) { if (host->id == SDIO_HOST_ID_1) { #ifdef CONFIG_TARGET_NA51000 *(uint32_t*) 0xF0010004 |= 0x4000; udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x3F0000); udelay(10); *(uint32_t*) 0xF00200A4 &= ~(0x4); udelay(10); *(uint32_t*) 0xF00200A4 |= 0x4; udelay(10); *(uint32_t*) 0xF0020020 &= ~0x30; udelay(10); *(uint32_t*) 0xF0020040 &= ~0x7FF; udelay(10); *(uint32_t*) 0xF0020040 |= 0x1F0; udelay(10); *(uint32_t*) 0xF0020084 |= 0x4; #elif (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64)) *(uint32_t*) 0xF0010004 &= ~0x10; // SDIO_EN udelay(10); *(uint32_t*) 0xF0010004 |= 0x10; udelay(10); *(uint32_t*) 0xF00100A8 &= ~(0x7B000); // PGPIO12~13, PGPIO15~18 udelay(10); *(uint32_t*) 0xF0020098 &= ~(0x4); // SDIO_RSTN udelay(10); *(uint32_t*) 0xF0020098 |= 0x4; udelay(10); *(uint32_t*) 0xF0020020 &= ~0x3; // SDIO_CLKSEL udelay(10); *(uint32_t*) 0xF0020040 &= ~0x7FF; // SDIO_CLKDIV udelay(10); *(uint32_t*) 0xF0020040 |= 0x1F0; udelay(10); *(uint32_t*) 0xF0020074 |= 0x800; // SDIO_CLKEN udelay(10); #elif (defined(CONFIG_TARGET_NA51102) || defined(CONFIG_TARGET_NA51102_A64)) *(uint32_t*) 0xF0010004 &= ~0x10; // SDIO_EN udelay(10); *(uint32_t*) 0xF0010004 |= 0x10; udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x3F << 11); // PGPIO12~13, PGPIO15~18 udelay(10); *(uint32_t*) 0xF0020094 &= ~(0x1 << 11); // SDIO_RSTN udelay(10); *(uint32_t*) 0xF0020094 |= (0x1 << 11); udelay(10); *(uint32_t*) 0xF0020020 &= ~0x3; // SDIO_CLKSEL udelay(10); *(uint32_t*) 0xF0020040 &= ~0x7FF; // SDIO_CLKDIV udelay(10); *(uint32_t*) 0xF0020040 |= 0x1F0; udelay(10); *(uint32_t*) 0xF0020074 |= 0x800; // SDIO_CLKEN udelay(10); #else *(uint32_t*) 0xF0010004 |= 0x4000; udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x1F800); udelay(10); *(uint32_t*) 0xF0020084 &= ~(0x4); udelay(10); *(uint32_t*) 0xF0020084 |= 0x4; udelay(10); *(uint32_t*) 0xF0020020 &= ~0x30; udelay(10); *(uint32_t*) 0xF002003C &= ~0x7FF; udelay(10); *(uint32_t*) 0xF002003C |= 0x1F0; udelay(10); *(uint32_t*) 0xF0020074 |= 0x4; udelay(10); *(uint32_t*) 0xF0030068 &= ~0xFFFFFF; udelay(10); *(uint32_t*) 0xF0030068 |= 0x111111; #endif } else if (host->id == SDIO_HOST_ID_2) { #ifdef CONFIG_TARGET_NA51089 if (host->pinmux_value & PIN_SDIO_CFG_2ND_PINMUX) { *(uint32_t*) 0xF0010004 &= ~0xC0000; *(uint32_t*) 0xF0010004 |= 0x80000; udelay(10); *(uint32_t*) 0xF00100B0 &= ~(0x18F); udelay(10); *(uint32_t*) 0xF003004C &= ~0x3C0FF; udelay(10); *(uint32_t*) 0xF003004C |= 0x1; udelay(10); } else if (host->pinmux_value & PIN_SDIO_CFG_3RD_PINMUX) { *(uint32_t*) 0xF0010004 &= ~0xC0000; *(uint32_t*) 0xF0010004 |= 0xC0000; udelay(10); *(uint32_t*) 0xF00100E8 &= ~(0xFC); udelay(10); *(uint32_t*) 0xF0030098 &= ~0xFFF0; udelay(10); *(uint32_t*) 0xF0030098 |= 0x10; udelay(10); } else { *(uint32_t*) 0xF0010004 &= ~0xC0000; *(uint32_t*) 0xF0010004 |= 0x40000; udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x7E0000); udelay(10); *(uint32_t*) 0xF0030068 &= ~0xF000000; udelay(10); *(uint32_t*) 0xF0030068 |= 0x1000000; udelay(10); *(uint32_t*) 0xF0030044 = 0x0; udelay(10); } #else *(uint32_t*) 0xF0010004 |= 0x8000; udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x7E0000); udelay(10); *(uint32_t*) 0xF0030068 &= ~0xF000000; udelay(10); *(uint32_t*) 0xF0030068 |= 0x1000000; udelay(10); *(uint32_t*) 0xF0030044 = 0x0; udelay(10); #endif *(uint32_t*) 0xF0020084 &= ~(0x8); udelay(10); *(uint32_t*) 0xF0020084 |= 0x8; udelay(10); *(uint32_t*) 0xF0020020 &= ~0x300; udelay(10); *(uint32_t*) 0xF002003C &= ~0x7FF0000; udelay(10); *(uint32_t*) 0xF002003C |= 0x1F00000; udelay(10); *(uint32_t*) 0xF0020074 |= 0x8; } else { *(uint32_t*) 0xF0010004 &= ~(0x80003000); udelay(10); if (host->enable_8bits) { *(uint32_t*) 0xF0010004 |= 0x80020000; //udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x3FF); } else { *(uint32_t*) 0xF0010004 |= 0x20000; udelay(10); *(uint32_t*) 0xF00100A0 &= ~(0x30F); } udelay(10); *(uint32_t*) 0xF0020084 &= ~(0x4000); udelay(10); *(uint32_t*) 0xF0020084 |= 0x4000; udelay(10); *(uint32_t*) 0xF0020074 &= ~0x1; udelay(10); *(uint32_t*) 0xF0020024 &= ~0x3; udelay(10); *(uint32_t*) 0xF0020040 &= ~0x7FF; udelay(10); *(uint32_t*) 0xF0020040 |= 0x1F0; udelay(10); *(uint32_t*) 0xF0020074 |= 0x4000; } sdiohost_enclkout(host, TRUE); sdiohost_setpaddriving(host, SDIO_MODE_DS); #if (defined(CONFIG_TARGET_NA51000) || defined(CONFIG_TARGET_NA51055)) sdiohost_setphysample(host, TRUE, FALSE); #else sdiohost_setphysample(host, FALSE, TRUE); #endif sdiohost_resetdata(host); /* Delay 1 ms (SD spec) after clock is outputted. */ /* (Delay 1024 us to reduce code size) */ udelay(1024); sdiohost_setreg(host, SDIO_INT_MASK_REG_OFS, 0xFF); sdiohost_setdatatimeout(host, 0x10000000); sdiohost_setreg(host, 0x1FC, 0x1); return E_OK; } #if !CONFIG_IS_ENABLED(DM_MMC) static int host_set_ios(struct mmc *dev) { struct mmc_nvt_host *host = dev->priv; #else static int host_set_ios(struct udevice *udev) { struct mmc *dev = mmc_get_mmc_dev(udev); struct mmc_nvt_host *host = dev_get_priv(udev); #endif //kwinyee debug //printf("%s called !,clk = %d, bwidth = %d\n",__func__, dev->clock, dev->bus_width); if (host->id >= 3) { printf("invalid host id %d\n", host->id); return -1; } if (dev->bus_width) { switch (dev->bus_width) { case 8: sdiohost_setbuswidth(host, SDIO_BUS_WIDTH8); break; case 4: sdiohost_setbuswidth(host, SDIO_BUS_WIDTH4); break; case 1: sdiohost_setbuswidth(host, SDIO_BUS_WIDTH1); break; } } if (dev->clock) { static int cur_clk[3] = {0}; if(dev->clock != cur_clk[host->id]) { sdiohost_setbusclk(host, dev->clock, (u32*)&host->ns_in_one_cycle); /*Set host sampling strategy according to different speeds and modes*/ if (dev->clock >= 192000000) { sdiohost_setphyclkindly(host, 0x9); sdiohost_setpaddriving(host, SDIO_MODE_SDR104); } else if (dev->clock >= 160000000) { sdiohost_setphyclkindly(host, 0x3); sdiohost_setpaddriving(host, SDIO_MODE_SDR104); } else if (dev->clock >= 120000000) { sdiohost_setphyclkindly(host, 0x1f); sdiohost_setpaddriving(host, SDIO_MODE_SDR104); } else if (dev->clock >= 96000000) { sdiohost_setphyclkindly(host, 0x1b); sdiohost_setpaddriving(host, SDIO_MODE_SDR50); } else if (dev->clock >= 64000000) { sdiohost_setphyclkindly(host, 0x0); sdiohost_setpaddriving(host, SDIO_MODE_SDR50); } else if (dev->clock >= 25000000) { sdiohost_setphyclkindly(host, 0x0); sdiohost_setpaddriving(host, SDIO_MODE_HS); } else { sdiohost_setphyclkindly(host, 0x0); sdiohost_setpaddriving(host, SDIO_MODE_DS); } if (host->indly_sel >= 0) { sdiohost_setphyclkindly(host, host->indly_sel); printf("use dts indly(0x%x)\n", host->indly_sel); } /*Reset phy to take effect*/ sdiohost_setphyrst(host); sdiohost_reset(host); } cur_clk[host->id] = dev->clock; host->mmc_input_clk = dev->clock; } return 0; } #if !CONFIG_IS_ENABLED(DM_MMC) static int mmc_host_getcd(struct mmc *mmc) #else static int mmc_host_getcd(struct udevice *dev) #endif { return 1; } static char dev_name_0[] = "NVT_MMC0"; static char dev_name_1[] = "NVT_MMC1"; static char dev_name_2[] = "NVT_MMC2"; #if !CONFIG_IS_ENABLED(DM_MMC) static const struct mmc_ops nvt_hsmmc_ops = { .send_cmd = host_request, .set_ios = host_set_ios, .init = mmc_host_reset, .getcd = mmc_host_getcd, }; /* * mmc_host_init - initialize the mmc controller. * Set initial clock and power for mmc slot. * Initialize mmc struct and register with mmc framework. */ int nvt_mmc_init(int id) { struct mmc *dev; struct mmc_nvt_host *host; struct mmc_config *cfg; host = malloc(sizeof(struct mmc_nvt_host)); if (!host) return -ENOMEM; memset(host, 0, sizeof(struct mmc_nvt_host)); cfg = malloc(sizeof(struct mmc_config)); if (!cfg) return -ENOMEM; memset(cfg, 0, sizeof(struct mmc_config)); //init host controler host->id = id; if (id == SDIO_HOST_ID_1) { host->base = (u32*)IOADDR_SDIO_REG_BASE; cfg->name = dev_name_0; } else if (id == SDIO_HOST_ID_2) { host->base = (u32*)IOADDR_SDIO2_REG_BASE; cfg->name = dev_name_1; } else { host->base = (u32*)IOADDR_SDIO3_REG_BASE; cfg->name = dev_name_2; } host->mmc_input_clk = 312500; host->mmc_default_clk = 48000000; mmc_nvt_parse_driving(host); nvt_emmc_arch_host_preinit(host); cfg->voltages = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 \ | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 \ | MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36; cfg->f_min = 312500; #ifdef CONFIG_NVT_FPGA_EMULATION cfg->f_max = 6000000; #else cfg->f_max = host->mmc_default_clk; #endif cfg->b_max = (32*1024); cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS; if(host->enable_8bits) cfg->host_caps |= MMC_MODE_8BIT; cfg->ops = &nvt_hsmmc_ops; dev = mmc_create(cfg, NULL); if (dev == NULL) { free(cfg); return -1; } dev->priv = host; debug("MMC DEBUG : %s done \n", __FUNCTION__); return 0; } #else #ifdef MMC_SUPPORTS_TUNING static int host_execute_tuning(struct udevice *dev, uint opcode) { return 0; } #endif static const struct dm_mmc_ops nvt_hsmmc_ops = { .send_cmd = host_request, .set_ios = host_set_ios, .get_cd = mmc_host_getcd, #ifdef MMC_SUPPORTS_TUNING .execute_tuning = host_execute_tuning, #endif }; static int nvt_mmc_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct nvt_mmc_plat *plat = dev_get_platdata(dev); struct mmc_nvt_host *host = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; u32 base_addr; host->base = (void*)dev_read_addr(dev); base_addr = (u32) host->base; if (base_addr == IOADDR_SDIO_REG_BASE) { host->id = SDIO_HOST_ID_1; cfg->name = dev_name_0; } else if (base_addr == IOADDR_SDIO2_REG_BASE) { host->id = SDIO_HOST_ID_2; cfg->name = dev_name_1; } else if (base_addr == IOADDR_SDIO3_REG_BASE) { host->id = SDIO_HOST_ID_3; cfg->name = dev_name_2; } host->mmc_input_clk = 312500; host->mmc_default_clk = 48000000; host->ext_caps = 0; host->do_dma = 1; mmc_nvt_parse_driving(host); nvt_emmc_arch_host_preinit(host); cfg->voltages = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 \ | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 \ | MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36 \ | MMC_VDD_165_195; #ifdef CONFIG_NVT_FPGA_EMULATION cfg->f_min = 312500; cfg->f_max = 12000000; #else cfg->f_min = 312500; cfg->f_max = host->mmc_default_clk; #endif cfg->b_max = (32*1024); cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS; if (host->enable_8bits) cfg->host_caps |= MMC_MODE_8BIT; if (host->ext_caps) { cfg->host_caps |= host->ext_caps; } upriv->mmc = &plat->mmc; return 0; } #if CONFIG_BLK static int nvt_mmc_bind(struct udevice *dev) { struct nvt_mmc_plat *plat = dev_get_platdata(dev); return mmc_bind(dev, &plat->mmc, &plat->cfg); } #endif static int nvt_mmc_remove(struct udevice *dev) { struct mmc_nvt_host *host = dev_get_priv(dev); if (host->id == SDIO_HOST_ID_1) { #ifdef CONFIG_SD_CARD1_POWER_PIN u32 reg_val; gpio_request(CONFIG_SD_CARD1_POWER_PIN, "sdio1_power"); gpio_request(C_GPIO(11), "sdio1_clk"); gpio_request(C_GPIO(12), "sdio1_cmd"); gpio_request(C_GPIO(13), "sdio1_d0"); gpio_request(C_GPIO(14), "sdio1_d1"); gpio_request(C_GPIO(15), "sdio1_d2"); gpio_request(C_GPIO(16), "sdio1_d3"); if (CONFIG_SD_CARD1_ON_STATE) gpio_direction_output(CONFIG_SD_CARD1_POWER_PIN, 0); else gpio_direction_output(CONFIG_SD_CARD1_POWER_PIN, 1); reg_val = readl(IOADDR_TOP_REG_BASE + 0xA0); reg_val |= 0x1F800; writel(reg_val, IOADDR_TOP_REG_BASE + 0xA0); reg_val = readl(IOADDR_PAD_REG_BASE); reg_val &= ~0xFFC00000; reg_val |= 0x55400000; writel(reg_val, IOADDR_PAD_REG_BASE); reg_val = readl(IOADDR_PAD_REG_BASE + 0x4); reg_val &= ~0x3; reg_val |= 0x1; writel(reg_val, IOADDR_PAD_REG_BASE + 0x4); gpio_direction_output(C_GPIO(11), 0); gpio_direction_output(C_GPIO(12), 0); gpio_direction_output(C_GPIO(13), 0); gpio_direction_output(C_GPIO(14), 0); gpio_direction_output(C_GPIO(15), 0); gpio_direction_output(C_GPIO(16), 0); /*Disable SDIO CLK*/ reg_val = readl(IOADDR_CG_REG_BASE + 0x74); reg_val &= ~(0x1 << 2); writel(reg_val, IOADDR_CG_REG_BASE + 0x74); #endif } else if (host->id == SDIO_HOST_ID_2) { #ifdef CONFIG_SD_CARD2_POWER_PIN u32 reg_val; int ret; gpio_request(CONFIG_SD_CARD2_POWER_PIN, "sdio2_power"); gpio_request(C_GPIO(17), "sdio2_clk"); gpio_request(C_GPIO(18), "sdio2_cmd"); gpio_request(C_GPIO(19), "sdio2_d0"); gpio_request(C_GPIO(20), "sdio2_d1"); gpio_request(C_GPIO(21), "sdio2_d2"); gpio_request(C_GPIO(22), "sdio2_d3"); if (CONFIG_SD_CARD2_ON_STATE) gpio_direction_output(CONFIG_SD_CARD2_POWER_PIN, 0); else gpio_direction_output(CONFIG_SD_CARD2_POWER_PIN, 1); reg_val = readl(IOADDR_TOP_REG_BASE + 0xA0); reg_val |= 0x7E0000; writel(reg_val, IOADDR_TOP_REG_BASE + 0xA0); reg_val = readl(IOADDR_PAD_REG_BASE + 0x4); reg_val &= ~0x3FFC; reg_val |= 0x1554; writel(reg_val, IOADDR_PAD_REG_BASE + 0x4); gpio_direction_output(C_GPIO(17), 0); gpio_direction_output(C_GPIO(18), 0); gpio_direction_output(C_GPIO(19), 0); gpio_direction_output(C_GPIO(20), 0); gpio_direction_output(C_GPIO(21), 0); gpio_direction_output(C_GPIO(22), 0); /*Disable SDIO CLK*/ reg_val = readl(IOADDR_CG_REG_BASE + 0x74); reg_val &= ~(0x1 << 3); writel(reg_val, IOADDR_CG_REG_BASE + 0x74); #endif } return 0; } static const struct udevice_id nvt_mmc_ids[] = { { .compatible = "nvt,nvt_mmc", }, { .compatible = "nvt,nvt_mmc2", }, { .compatible = "nvt,nvt_mmc3", }, {}, }; U_BOOT_DRIVER(nvt_mmc_drv) = { .name = "nvtivot_mmc", .id = UCLASS_MMC, .of_match = nvt_mmc_ids, #if CONFIG_BLK .bind = nvt_mmc_bind, #endif .probe = nvt_mmc_probe, .ops = &nvt_hsmmc_ops, .platdata_auto_alloc_size = sizeof(struct nvt_mmc_plat), .priv_auto_alloc_size = sizeof(struct mmc_nvt_host), .remove = nvt_mmc_remove, .flags = DM_FLAG_OS_PREPARE, }; #endif