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
|