nt9856x/BSP/u-boot/drivers/mmc/nvt_ivot_mmc_a64.c
2023-03-28 15:07:53 +08:00

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