2307 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2307 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  *  driver/mmc/nvt_ivot_mmc.c
 | |
|  *
 | |
|  *  Author:	Howard Chang
 | |
|  *  Created:	Feb 01, 2021
 | |
|  *  Copyright:	Novatek Inc.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <asm/io.h>
 | |
| #include "common.h"
 | |
| #include <errno.h>
 | |
| #include <dm.h>
 | |
| #include <command.h>
 | |
| #include <mmc.h>
 | |
| #include <malloc.h>
 | |
| #include "nvt_ivot_mmc.h"
 | |
| #ifdef CONFIG_TARGET_NA51055
 | |
| #include <asm/arch/na51055_regs.h>
 | |
| #else
 | |
| #ifdef CONFIG_TARGET_NA51089
 | |
| #include <asm/arch/na51089_regs.h>
 | |
| #else
 | |
| #if (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64))
 | |
| #include <asm/arch/na51090_regs.h>
 | |
| #elif (defined(CONFIG_TARGET_NA51102) || defined(CONFIG_TARGET_NA51102_A64))
 | |
| #include <asm/arch/na51102_regs.h>
 | |
| #elif (defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64))
 | |
| #include <asm/arch/na51103_regs.h>
 | |
| #else
 | |
| #include <asm/arch/na51000_regs.h>
 | |
| #endif
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #include <asm/nvt-common/nvt_types.h>
 | |
| #include <asm/nvt-common/nvt_common.h>
 | |
| #include <linux/libfdt.h>
 | |
| //#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_NA51000_A64) || defined(CONFIG_TARGET_NA51102_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64))
 | |
| #define PLL_SYS_CR_REG_OFS	(IOADDR_CG_REG_BASE + 0x40)
 | |
| #else
 | |
| #define PLL_SYS_CR_REG_OFS	(IOADDR_CG_REG_BASE + 0x3C)
 | |
| #endif
 | |
| 
 | |
| #define SDIO_MASK		0x7FF
 | |
| #define SDIO2_MASK		0x7FF0000
 | |
| 
 | |
| #ifdef CONFIG_NVT_FPGA_EMULATION
 | |
| #define FPGA_SDIO_SRCCLK	12000000
 | |
| #else
 | |
| #define FPGA_SDIO_SRCCLK	192000000
 | |
| #endif
 | |
| 
 | |
| #define DDR_MASK                0xF00000000
 | |
| #define ADDR_MAU2               0x1
 | |
| #define ADDR_PCIE_MAU1          0x4
 | |
| #define ADDR_PCIE_MAU2          0x5
 | |
| #define SDIO_HIGH_ADDR_REG_OFS  0xF0
 | |
| 
 | |
| 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] = {2, 1, 1, 2, 1, 1, 3, 2, 2, 3, 2, 2};
 | |
| 
 | |
| 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 (defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51102_A64) || defined(CONFIG_TARGET_NA51103_A64))
 | |
| 	if (host->id == SDIO_HOST_ID_1) {
 | |
| 		sprintf(path,"/mmc@%lx",(IOADDR_SDIO_REG_BASE & 0xFFFFFFFF));
 | |
| 	} else if (host->id == SDIO_HOST_ID_2) {
 | |
| 		sprintf(path,"/mmc@%lx",(IOADDR_SDIO2_REG_BASE & 0xFFFFFFFF));
 | |
| 	}
 | |
| #else
 | |
| 	if (host->id == SDIO_HOST_ID_1) {
 | |
| 		sprintf(path,"/mmc@%x",(IOADDR_SDIO_REG_BASE & 0xFFFFFFFF));
 | |
| 	} else if (host->id == SDIO_HOST_ID_2) {
 | |
| 		sprintf(path,"/mmc@%x",(IOADDR_SDIO2_REG_BASE & 0xFFFFFFFF));
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	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 Use default driving table\n");
 | |
| 	} 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_2) {
 | |
| #if (defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51102_A64) || defined(CONFIG_TARGET_NA51103_A64))
 | |
| 		sprintf(path,"/top@%lx/sdio",(IOADDR_TOP_REG_BASE & 0xFFFFFFFF));
 | |
| #else
 | |
| 		sprintf(path,"/top@%x/sdio",(IOADDR_TOP_REG_BASE & 0xFFFFFFFF));
 | |
| #endif
 | |
| 		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]);
 | |
| 
 | |
| 		printf("%s: sdio pinmux(0x%x)\n", __func__, host->pinmux_value);
 | |
| 
 | |
| 		if (host->pinmux_value & PIN_SDIO2_CFG_BUS_WIDTH)
 | |
| 			host->enable_8bits = 1;
 | |
| 		else
 | |
| 			host->enable_8bits = 0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //=============================================================================================================
 | |
| 
 | |
| /*
 | |
| 	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;
 | |
| 
 | |
| 	regdata = readl(PLL_SYS_CR_REG_OFS + 0x4);
 | |
| 
 | |
| 	if (id == SDIO_HOST_ID_1)
 | |
| 		regdata &= SDIO_MASK;
 | |
| 	else if (id == SDIO_HOST_ID_2)
 | |
| 		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;
 | |
| 
 | |
| 	regdata = readl(PLL_SYS_CR_REG_OFS);
 | |
| 
 | |
| 	if (id == SDIO_HOST_ID_1) {
 | |
| 		regdata &= ~SDIO_MASK;
 | |
| 		regdata |= value;
 | |
| 	} else if (id == SDIO_HOST_ID_2){
 | |
| 		regdata &= ~SDIO2_MASK;
 | |
| 		regdata |= value << 16;
 | |
| 	}
 | |
| 
 | |
| 	writel(regdata, PLL_SYS_CR_REG_OFS);
 | |
| }
 | |
| 
 | |
| /*
 | |
| 	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;
 | |
| 	unsigned long driving = paddriving;
 | |
| 
 | |
| 	bitoffset = name & 0x1F;
 | |
| 	dwoffset = (name >> 5);
 | |
| 
 | |
| 	HAL_READ_UINT32(IOADDR_PAD_REG_BASE + PAD_PUPD0_REG_OFS + (dwoffset << 2), padreg);
 | |
| 	padreg &= ~(0xf << bitoffset);
 | |
| 	padreg |=  (driving << bitoffset);
 | |
| 	HAL_WRITE_UINT32(IOADDR_PAD_REG_BASE + PAD_PUPD0_REG_OFS + (dwoffset << 2), padreg);
 | |
| 
 | |
| 	return E_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
| 	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 = host->pad_driving[SDIO_DS_MODE_DATA];
 | |
| 		cmd_uidriving = host->pad_driving[SDIO_DS_MODE_CMD];
 | |
| 		clk_uidriving = host->pad_driving[SDIO_DS_MODE_CLK];
 | |
| 	} else if (mode == SDIO_MODE_HS) {
 | |
| 		data_uidriving = host->pad_driving[SDIO_HS_MODE_DATA];
 | |
| 		cmd_uidriving = host->pad_driving[SDIO_HS_MODE_CMD];
 | |
| 		clk_uidriving = host->pad_driving[SDIO_HS_MODE_CLK];
 | |
| 	} else if (mode == SDIO_MODE_SDR50) {
 | |
| 		data_uidriving = host->pad_driving[SDIO_SDR50_MODE_DATA];
 | |
| 		cmd_uidriving = host->pad_driving[SDIO_SDR50_MODE_CMD];
 | |
| 		clk_uidriving = host->pad_driving[SDIO_SDR50_MODE_CLK];
 | |
| 	} else {
 | |
| 		data_uidriving = host->pad_driving[SDIO_SDR104_MODE_DATA];
 | |
| 		cmd_uidriving = host->pad_driving[SDIO_SDR104_MODE_CMD];
 | |
| 		clk_uidriving = host->pad_driving[SDIO_SDR104_MODE_CLK];
 | |
| 	}
 | |
| 
 | |
| 	if (host->id == SDIO_HOST_ID_1) {
 | |
| 		pad_setdrivingsink(PAD_DS_PGPIO13, cmd_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_PGPIO15, data_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_PGPIO16, data_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_PGPIO17, data_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_PGPIO18, data_uidriving);
 | |
| 		return pad_setdrivingsink(PAD_DS_PGPIO12, clk_uidriving);
 | |
| 	} else if (host->id == SDIO_HOST_ID_2) {
 | |
| 		if (host->enable_8bits) {
 | |
| 			pad_setdrivingsink(PAD_DS_CGPIO2, data_uidriving);
 | |
| 			pad_setdrivingsink(PAD_DS_CGPIO3, data_uidriving);
 | |
| 			pad_setdrivingsink(PAD_DS_CGPIO4, data_uidriving);
 | |
| 			pad_setdrivingsink(PAD_DS_CGPIO5, data_uidriving);
 | |
| 		}
 | |
| 
 | |
| 		pad_setdrivingsink(PAD_DS_CGPIO9, cmd_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_CGPIO10, data_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_CGPIO11, data_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_CGPIO12, data_uidriving);
 | |
| 		pad_setdrivingsink(PAD_DS_CGPIO13, 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 ,
 | |
| unsigned long uidmaaddress, unsigned long 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;
 | |
| 	unsigned long 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 = (unsigned long)virt_to_phys((void*)uidmaaddress);
 | |
| 	sdiohost_setreg(host, SDIO_DMA_START_ADDR_REG_OFS, dmaaddrreg.reg);
 | |
| 
 | |
| 	if (uidmaaddress & DDR_MASK) {
 | |
| 		unsigned long addr_shift = (uidmaaddress & DDR_MASK) >> 32;
 | |
| 
 | |
| 		sdiohost_setreg(host, SDIO_HIGH_ADDR_REG_OFS, addr_shift);
 | |
| 	} else
 | |
| 		sdiohost_setreg(host, SDIO_HIGH_ADDR_REG_OFS, 0);
 | |
| 
 | |
| 	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 ((unsigned long)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 ((unsigned long)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;
 | |
| 	long end_command = 0;
 | |
| 	long end_transfer = 0;
 | |
| 	long 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;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	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) {
 | |
| 			debug("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) {
 | |
| 		debug("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)) {
 | |
| 		/* End of command phase */
 | |
| 		if (data == NULL)
 | |
| 			end_command = (unsigned long) 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 = (unsigned long) host->cmd;
 | |
| 
 | |
| 	if (data && !host->do_dma) {
 | |
| 		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;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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)
 | |
| {
 | |
| 	unsigned long 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) || ((unsigned long)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) || ((unsigned long)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 = (unsigned long)(host->buffer) + host->bytes_left;
 | |
| 
 | |
| 		if (host->data_dir == SDIO_HOST_WRITE_DATA)
 | |
| 			flush_dcache_range(ALIGN_FLOOR((unsigned long)host->buffer, ARCH_DMA_MINALIGN), (unsigned long)roundup(size,ARCH_DMA_MINALIGN));
 | |
| 		else
 | |
| 			invalidate_dcache_range(ALIGN_FLOOR((unsigned long)host->buffer, ARCH_DMA_MINALIGN), (unsigned long)roundup(size,ARCH_DMA_MINALIGN));
 | |
| 
 | |
| 		sdiohost_setupdatatransferdma(host, (unsigned long)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_2)) {
 | |
| #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_2) {
 | |
| 				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)
 | |
| {
 | |
| 	unsigned long reg_value;
 | |
| 	if (host->id == SDIO_HOST_ID_1) {
 | |
| #if (defined(CONFIG_TARGET_NA51000) || defined(CONFIG_TARGET_NA51000_A64))
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x4000;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		reg_value &= ~(0x3F0000);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0xA4);
 | |
| 		reg_value &= ~(0x4);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0xA4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0xA4);
 | |
| 		reg_value |= 0x4;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0xA4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);
 | |
| 		reg_value &= ~0x30;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value &= ~0x7FF;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value |= 0x1F0;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value |= 0x4;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| #elif (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64))
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);    // SDIO_EN
 | |
| 		reg_value &= ~0x10;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x10;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0xA8);   // PGPIO12~13, PGPIO15~18
 | |
| 		reg_value &= ~(0x7B000);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0xA8);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x98);    // SDIO_RSTN
 | |
| 		reg_value &= ~(0x4);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x98);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x98);
 | |
| 		reg_value |= 0x4;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x98);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);    // SDIO_CLKSEL
 | |
| 		reg_value &= ~0x3;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);    // SDIO_CLKDIV
 | |
| 		reg_value &= ~0x7FF;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value |= 0x1F0;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);    // SDIO_CLKEN
 | |
| 		reg_value |= 0x800;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| 	#ifndef CONFIG_NVT_FPGA_EMULATION
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x64);   // Driving
 | |
| 		reg_value &= ~0xF0FF0000;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x64);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x64);
 | |
| 		reg_value |= 0x10110000;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x64);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		reg_value &= ~0xFFF;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		reg_value |= 0x111;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x68);
 | |
| 	#endif
 | |
| #elif (defined(CONFIG_TARGET_NA51102_A64))
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);    // SDIO_EN
 | |
| 		reg_value &= ~0x10;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x10;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);   // PGPIO12~13, PGPIO15~18
 | |
| 		reg_value &= ~(0x3F << 11);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x94);    // SDIO_RSTN
 | |
| 		reg_value &= ~(0x1 << 11);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x94);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x94);
 | |
| 		reg_value |= (0x1 << 11);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x94);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);    // SDIO_CLKSEL
 | |
| 		reg_value &= ~0x3;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);    // SDIO_CLKDIV
 | |
| 		reg_value &= ~0x7FF;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value |= 0x1F0;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);    // SDIO_CLKEN
 | |
| 		reg_value |= 0x800;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| #else
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x4000;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		reg_value &= ~(0x1F800);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value &= ~(0x4);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value |= 0x4;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);
 | |
| 		reg_value &= ~0x30;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		reg_value &= ~0x7FF;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		reg_value |= 0x1F0;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);
 | |
| 		reg_value |= 0x4;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		reg_value &= ~0xFFFFFF;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		reg_value |= 0x111111;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x68);
 | |
| #endif
 | |
| 	} else if (host->id == SDIO_HOST_ID_2) {
 | |
| #if (defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64))
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);    // SDIO2_EN
 | |
| 		reg_value &= ~0x100;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x100;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		if (host->enable_8bits) {
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 			reg_value |= 0x400;
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 			udelay(10);
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);   // CGPIO8~13, CGPIO2~5
 | |
| 			reg_value &= ~(0x3F3C);
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		} else {
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);   // CGPIO8~13
 | |
| 			reg_value &= ~(0x3F00);
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		}
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x98);    // SDIO2_RSTN
 | |
| 		reg_value &= ~(0x8);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x98);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x98);
 | |
| 		reg_value |= 0x8;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x98);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);    // SDIO2_CLKSEL
 | |
| 		reg_value &= ~0xC;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);    // SDIO2_CLKDIV
 | |
| 		reg_value &= ~0x7FF0000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value |= 0x1F00000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);    // SDIO2_CLKEN
 | |
| 		reg_value |= 0x1000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| 	#ifndef CONFIG_NVT_FPGA_EMULATION
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x54);   // Driving
 | |
| 		reg_value &= ~0xFFFFFF;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x54);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x54);
 | |
| 		reg_value |= 0x111111;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x54);
 | |
| 		udelay(10);
 | |
| 		if (host->enable_8bits) {
 | |
| 			reg_value = readl(IOADDR_PAD_REG_BASE + 0x50);
 | |
| 			reg_value &= ~0xFFFF00;
 | |
| 			writel(reg_value, IOADDR_PAD_REG_BASE + 0x50);
 | |
| 			udelay(10);
 | |
| 			reg_value = readl(IOADDR_PAD_REG_BASE + 0x50);
 | |
| 			reg_value |= 0x111100;
 | |
| 			writel(reg_value, IOADDR_PAD_REG_BASE + 0x50);
 | |
| 		}
 | |
| 	#endif
 | |
| #elif (defined(CONFIG_TARGET_NA51102_A64))
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);    // SDIO_EN
 | |
| 		reg_value &= ~0x10;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x10;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);   // PGPIO12~13, PGPIO15~18
 | |
| 		reg_value &= ~(0x3F << 11);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x94);    // SDIO_RSTN
 | |
| 		reg_value &= ~(0x1 << 11);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x94);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x94);
 | |
| 		reg_value |= (0x1 << 11);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x94);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);    // SDIO_CLKSEL
 | |
| 		reg_value &= ~0x3;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);    // SDIO_CLKDIV
 | |
| 		reg_value &= ~0x7FF;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value |= 0x1F0;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);    // SDIO_CLKEN
 | |
| 		reg_value |= 0x800;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| #else
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value |= 0x8000;
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		reg_value &= ~(0x7E0000);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value &= ~(0x8);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value |= 0x8;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x20);
 | |
| 		reg_value &= ~0x300;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x20);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		reg_value &= ~0x7FF000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		reg_value |= 0x1F0000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x3C);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);
 | |
| 		reg_value |= 0x8;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		reg_value &= ~0xF000000;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		reg_value |= 0x1000000;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x68);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_PAD_REG_BASE + 0x44);
 | |
| 		reg_value = 0x0;
 | |
| 		writel(reg_value, IOADDR_PAD_REG_BASE + 0x44);
 | |
| #endif
 | |
| 	} else {
 | |
| #if (!(defined(CONFIG_TARGET_NA51090) || defined(CONFIG_TARGET_NA51090_A64) || defined(CONFIG_TARGET_NA51103) || defined(CONFIG_TARGET_NA51103_A64)))
 | |
| 		reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		reg_value &= ~(0x80003000);
 | |
| 		writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 		udelay(10);
 | |
| 		if (host->enable_8bits) {
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 			reg_value |= 0x80020000;
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 			udelay(10);
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 			reg_value &= ~(0x3FF);
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		} else {
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 			reg_value |= 0x20000;
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0x4);
 | |
| 			udelay(10);
 | |
| 			reg_value = readl(IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 			reg_value &= ~(0x30F);
 | |
| 			writel(reg_value, IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 		}
 | |
| 
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value &= ~(0x4000);
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg_value |= 0x4000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x84);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);
 | |
| 		reg_value &= ~0x1;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x24);
 | |
| 		reg_value &= ~0x3;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x24);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value &= ~0x7FF;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg_value |= 0x1F0;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x40);
 | |
| 		udelay(10);
 | |
| 		reg_value = readl(IOADDR_CG_REG_BASE + 0x74);
 | |
| 		reg_value |= 0x4000;
 | |
| 		writel(reg_value, IOADDR_CG_REG_BASE + 0x74);
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	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, 0x0);
 | |
| 				sdiohost_setpaddriving(host, SDIO_MODE_SDR104);
 | |
| 			} else if (dev->clock >= 160000000) {
 | |
| 				sdiohost_setphyclkindly(host, 0x0);
 | |
| 				sdiohost_setpaddriving(host, SDIO_MODE_SDR104);
 | |
| 			} else if (dev->clock >= 120000000) {
 | |
| 				sdiohost_setphyclkindly(host, 0x0);
 | |
| 				sdiohost_setpaddriving(host, SDIO_MODE_SDR104);
 | |
| 			} else if (dev->clock >= 96000000) {
 | |
| 				sdiohost_setphyclkindly(host, 0x0);
 | |
| 				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";
 | |
| 
 | |
| #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 = IOADDR_SDIO_REG_BASE;
 | |
| 		cfg->name = dev_name_0;
 | |
| 	} else if (id == SDIO_HOST_ID_2) {
 | |
| 		host->base = IOADDR_SDIO2_REG_BASE;
 | |
| 		cfg->name = dev_name_1;
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 	unsigned long base_addr;
 | |
| 
 | |
| 	host->base = (void*)dev_read_addr(dev);
 | |
| 	base_addr = (unsigned long) host->base;
 | |
| 	if (base_addr == (IOADDR_SDIO_REG_BASE)) {
 | |
| 		host->id = SDIO_HOST_ID_1;
 | |
| 		cfg->name = dev_name_0;
 | |
| 		host->base = (void*)IOADDR_SDIO_REG_BASE;
 | |
| 
 | |
| 	} else if (base_addr == (IOADDR_SDIO2_REG_BASE)) {
 | |
| 		host->id = SDIO_HOST_ID_2;
 | |
| 		cfg->name = dev_name_1;
 | |
| 		host->base = (void*)IOADDR_SDIO2_REG_BASE;
 | |
| 	}
 | |
| 
 | |
| 	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(P_GPIO(12), "sdio1_clk");
 | |
| 		gpio_request(P_GPIO(13), "sdio1_cmd");
 | |
| 		gpio_request(P_GPIO(15), "sdio1_d0");
 | |
| 		gpio_request(P_GPIO(16), "sdio1_d1");
 | |
| 		gpio_request(P_GPIO(17), "sdio1_d2");
 | |
| 		gpio_request(P_GPIO(18), "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 + 0xA8);
 | |
| 		reg_val |= 0x7B000;
 | |
| 		writel(reg_val, IOADDR_TOP_REG_BASE + 0xA8);
 | |
| 
 | |
| 		reg_val = readl(IOADDR_PAD_REG_BASE + 0x08);
 | |
| 		reg_val &= ~0xCF000000;
 | |
| 		reg_val |= 0x45000000;
 | |
| 		writel(reg_val, IOADDR_PAD_REG_BASE + 0x08);
 | |
| 
 | |
| 		reg_val = readl(IOADDR_PAD_REG_BASE + 0x0C);
 | |
| 		reg_val &= ~0x3F;
 | |
| 		reg_val |= 0x15;
 | |
| 		writel(reg_val, IOADDR_PAD_REG_BASE + 0x0C);
 | |
| 
 | |
| 		gpio_direction_output(P_GPIO(12), 0);
 | |
| 		gpio_direction_output(P_GPIO(13), 0);
 | |
| 		gpio_direction_output(P_GPIO(15), 0);
 | |
| 		gpio_direction_output(P_GPIO(16), 0);
 | |
| 		gpio_direction_output(P_GPIO(17), 0);
 | |
| 		gpio_direction_output(P_GPIO(18), 0);
 | |
| 
 | |
| 		/*Disable SDIO CLK*/
 | |
| 		reg_val = readl(IOADDR_CG_REG_BASE + 0x74);
 | |
| 		reg_val &= ~(0x1 << 11);
 | |
| 		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",
 | |
| 	},
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| 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
 | 
