2374 lines
65 KiB
C
Executable File
2374 lines
65 KiB
C
Executable File
/**
|
|
NVT mmc function
|
|
To handle mmc modules
|
|
@file na51000_mmchost.c
|
|
@ingroup
|
|
@note
|
|
Copyright Novatek Microelectronics Corp. 2016. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2 as
|
|
published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include "na51055_mmchost.h"
|
|
#include "na51055_mmcreg.h"
|
|
#include <linux/io.h>
|
|
#include <linux/printk.h>
|
|
#include <mach/nvt-io.h>
|
|
#include <linux/slab.h>
|
|
|
|
#define RESET_TIMEOUT 10000
|
|
/************************* cache API ********************************/
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
static struct mmc_nvt_host drvdump_info[3];
|
|
|
|
void dma_flushwritecache(struct device *dev, void *pbuf, u32 len)
|
|
{
|
|
dma_addr_t dma_handle;
|
|
/*void *ptr;*/
|
|
/*printk("flash write buf 0x%x, len %d\r\n", pbuf, len);*/
|
|
/*for (ptr = pbuf; ptr < (pbuf + len + PAGE_SIZE); ptr += PAGE_SIZE)
|
|
flush_dcache_page(virt_to_page(ptr));*/
|
|
dma_handle = dma_map_single(dev, pbuf, len, DMA_TO_DEVICE);
|
|
|
|
dma_sync_single_for_device(dev, dma_handle, len, DMA_TO_DEVICE);
|
|
|
|
dma_unmap_single(dev, dma_handle, len, DMA_TO_DEVICE);
|
|
}
|
|
|
|
void dma_flushreadcache(struct device *dev, void *pbuf, u32 len)
|
|
{
|
|
dma_addr_t dma_handle;
|
|
/*void *ptr;*/
|
|
/*printk("flash read buf 0x%x, len %d\r\n", pbuf, len);*/
|
|
/*for (ptr = pbuf; ptr < (pbuf + len + PAGE_SIZE); ptr += PAGE_SIZE)
|
|
flush_dcache_page(virt_to_page(ptr));*/
|
|
dma_handle = dma_map_single(dev, pbuf, len, DMA_FROM_DEVICE);
|
|
|
|
dma_sync_single_for_device(dev, dma_handle, len, DMA_FROM_DEVICE);
|
|
|
|
dma_unmap_single(dev, dma_handle, len, DMA_FROM_DEVICE);
|
|
}
|
|
|
|
void dma_flushbidircache(struct device *dev, void *pbuf, u32 len)
|
|
{
|
|
dma_addr_t dma_handle;
|
|
/*void *ptr;*/
|
|
/*printk("flash read buf 0x%x, len %d\r\n", pbuf, len);*/
|
|
/*for (ptr = pbuf; ptr < (pbuf + len + PAGE_SIZE); ptr += PAGE_SIZE)
|
|
flush_dcache_page(virt_to_page(ptr));*/
|
|
dma_handle = dma_map_single(dev, pbuf, len, DMA_BIDIRECTIONAL);
|
|
|
|
dma_sync_single_for_device(dev, dma_handle, len, DMA_BIDIRECTIONAL);
|
|
|
|
dma_unmap_single(dev, dma_handle, len, DMA_BIDIRECTIONAL);
|
|
}
|
|
|
|
/************************* cache API ********************************/
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------*/
|
|
/* Internal function prototypes */
|
|
/*-------------------------------------------------------*/
|
|
static REGVALUE sdiohost_getreg(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t offset);
|
|
static void sdiohost_setreg(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t offset, REGVALUE value);
|
|
|
|
/*-------------------------------------------------------*/
|
|
/* Private driver specific variables */
|
|
/*-------------------------------------------------------*/
|
|
static uint32_t vsdio_seg_en[SDIO_HOST_ID_COUNT] = {
|
|
FALSE,
|
|
#if (SDIO_HOST_ID_COUNT > 1)
|
|
FALSE,
|
|
#endif
|
|
#if (SDIO_HOST_ID_COUNT > 2)
|
|
FALSE
|
|
#endif
|
|
};
|
|
|
|
static uint32_t vsdio_seg_num[SDIO_HOST_ID_COUNT] = {
|
|
1,
|
|
#if (SDIO_HOST_ID_COUNT > 1)
|
|
1,
|
|
#endif
|
|
#if (SDIO_HOST_ID_COUNT > 2)
|
|
1
|
|
#endif
|
|
};
|
|
|
|
#if (SDIO_SCATTER_DMA)
|
|
#define SDIO_DES_MAX_BUF_SIZE (64*1024*1024) /*64MB*/
|
|
|
|
/*static uint32_t
|
|
vuisdio_destab[SDIO_HOST_ID_COUNT][SDIO_DES_TABLE_NUM*SDIO_DES_WORD_SIZE];*/
|
|
#endif
|
|
|
|
/*********************************************************************/
|
|
/* Public(to driver layer) SDIO host controller functions */
|
|
/*********************************************************************/
|
|
|
|
void sdiohost_setdesen(uint32_t id, uint32_t uien)
|
|
{
|
|
if (uien == 0)
|
|
vsdio_seg_en[id] = FALSE;
|
|
else
|
|
vsdio_seg_en[id] = TRUE;
|
|
}
|
|
|
|
void sdiohost_setdestab(uint32_t id, uint32_t uidesaddr, uint32_t uidesnum,
|
|
uint32_t *vuisdio_destab)
|
|
{
|
|
#if (SDIO_SCATTER_DMA)
|
|
/*union SDIO_DATA_LENGTH_REG datalenreg; */
|
|
/*union SDIO_DMA_DES_START_ADDR_REG dmadesaddrreg; */
|
|
union SDIO_DES_LINE_REG dmadeslinereg;
|
|
union SDIO_DES_LINE1_REG dmadesline1reg;
|
|
union SDIO_DES_LINE2_REG dmadesline2reg;
|
|
uint32_t uitotalsize;
|
|
struct STRG_SEG_DES *pseg_des;
|
|
uint32_t i;
|
|
|
|
if (uidesnum > SDIO_DES_TABLE_NUM) {
|
|
pr_err("Descriptor number must < SDIO_DES_TABLE_NUM\r\n");
|
|
return;
|
|
}
|
|
|
|
memset((void *)&vuisdio_destab[0], 0,
|
|
(SDIO_DES_TABLE_NUM * SDIO_DES_WORD_SIZE)<<2);
|
|
|
|
uitotalsize = 0;
|
|
pseg_des = (struct STRG_SEG_DES *)uidesaddr;
|
|
|
|
for (i = 0; i < uidesnum; i++) {
|
|
dmadeslinereg.reg = 0;
|
|
dmadesline1reg.reg = 0;
|
|
dmadesline2reg.reg = 0;
|
|
|
|
dmadeslinereg.bit.DMA_DES_VALID = 1;
|
|
|
|
if (i == (uidesnum - 1))
|
|
dmadeslinereg.bit.DMA_DES_END = 1;
|
|
else
|
|
dmadeslinereg.bit.DMA_DES_END = 0;
|
|
|
|
dmadeslinereg.bit.DMA_DES_ACT = 2;
|
|
|
|
dmadesline1reg.bit.DMA_DES_DATA_LEN = pseg_des->uisegsize;
|
|
dmadesline2reg.bit.DMA_DES_DATA_ADDR = pseg_des->uisegaddr;
|
|
|
|
vuisdio_destab[(i * SDIO_DES_WORD_SIZE) +
|
|
SDIO_DES_LINE_REG_OFS] = (uint32_t)dmadeslinereg.reg;
|
|
|
|
vuisdio_destab[(i * SDIO_DES_WORD_SIZE) +
|
|
(SDIO_DES_LINE1_REG_OFS >> 2)] = (uint32_t)dmadesline1reg.reg;
|
|
|
|
vuisdio_destab[(i * SDIO_DES_WORD_SIZE) +
|
|
(SDIO_DES_LINE2_REG_OFS >> 2)] = (uint32_t)dmadesline2reg.reg;
|
|
|
|
pseg_des++;
|
|
uitotalsize = dmadesline1reg.bit.DMA_DES_DATA_LEN;
|
|
|
|
}
|
|
vsdio_seg_num[id] = uidesnum;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
Get SDIO Busy or not
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return TRUE: ready
|
|
FALSE: busy
|
|
*/
|
|
bool sdiohost_getrdy(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_BUS_STATUS_REG stsreg;
|
|
|
|
stsreg.reg = sdiohost_getreg(host, id, SDIO_BUS_STATUS_REG_OFS);
|
|
|
|
return stsreg.bit.CARD_BUSY;
|
|
}
|
|
|
|
/*
|
|
Enable CKGEN clock for specified SDIO channel.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_enclock(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
clk_enable(host->clk);
|
|
}
|
|
|
|
/*
|
|
Disable CKGEN clock for specified SDIO channel.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_disclock(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
clk_disable(host->clk);
|
|
}
|
|
|
|
|
|
/*
|
|
Enable SD clock for specified SDIO channel.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_enclockout(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_CLOCK_CTRL_REG clk_ctrl;
|
|
|
|
clk_ctrl.reg = sdiohost_getreg(host, id, SDIO_CLOCK_CTRL_REG_OFS);
|
|
clk_ctrl.bit.CLK_DIS = 0;
|
|
sdiohost_setreg(host, id, SDIO_CLOCK_CTRL_REG_OFS, clk_ctrl.reg);
|
|
}
|
|
|
|
/*
|
|
Disable SD clock for specified SDIO channel.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_disclockout(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_CLOCK_CTRL_REG clk_ctrl;
|
|
clk_ctrl.reg = sdiohost_getreg(host, id, SDIO_CLOCK_CTRL_REG_OFS);
|
|
clk_ctrl.bit.CLK_DIS = 1;
|
|
sdiohost_setreg(host, id, SDIO_CLOCK_CTRL_REG_OFS, clk_ctrl.reg);
|
|
}
|
|
|
|
void sdiohost_setphyrst(struct mmc_nvt_host *host)
|
|
{
|
|
union SDIO_PHY_REG phyreg, phyreg_read;
|
|
u8 i;
|
|
|
|
phyreg.reg = sdiohost_getreg(host, host->id, SDIO_PHY_REG_OFS);
|
|
phyreg.bit.PHY_SW_RST = 1;
|
|
sdiohost_setreg(host, host->id, SDIO_PHY_REG_OFS, phyreg.reg);
|
|
while (1)
|
|
{
|
|
phyreg_read.reg = sdiohost_getreg(host, host->id, SDIO_PHY_REG_OFS);
|
|
if ((phyreg_read.bit.PHY_SW_RST == 0) || (i == RESET_TIMEOUT))
|
|
break;
|
|
|
|
i++;
|
|
}
|
|
|
|
if (i == RESET_TIMEOUT)
|
|
pr_err("phy reset timeout\n");
|
|
}
|
|
|
|
void sdiohost_setphysample(struct mmc_nvt_host *host, bool internal, bool before)
|
|
{
|
|
union SDIO_DLY0_REG dly0_reg;
|
|
|
|
dly0_reg.reg = sdiohost_getreg(host, host->id, SDIO_DLY0_REG_OFS);
|
|
dly0_reg.bit.SAMPLE_CLK_EDGE = host->neg_sample_edge;
|
|
dly0_reg.bit.SRC_CLK_SEL = internal;
|
|
dly0_reg.bit.PAD_CLK_SEL = before;
|
|
sdiohost_setreg(host, host->id, SDIO_DLY0_REG_OFS, dly0_reg.reg);
|
|
}
|
|
|
|
void sdiohost_setinvoutdly(struct mmc_nvt_host *host, bool data_outdly_inv, bool cmd_outdly_inv)
|
|
{
|
|
union SDIO_DLY5_REG dly5_reg;
|
|
|
|
dly5_reg.reg = sdiohost_getreg(host, host->id, SDIO_DLY5_REG_OFS);
|
|
dly5_reg.bit.DATA_OUT_DLY_INV = data_outdly_inv;
|
|
dly5_reg.bit.CMD_OUT_DLY_INV = cmd_outdly_inv;
|
|
|
|
sdiohost_setreg(host, host->id, SDIO_DLY5_REG_OFS, dly5_reg.reg);
|
|
}
|
|
|
|
void sdiohost_setphyclkoutdly(struct mmc_nvt_host *host, uint32_t outdly_sel)
|
|
{
|
|
union SDIO_CLOCK_CTRL2_REG clock_ctrl;
|
|
|
|
clock_ctrl.reg = sdiohost_getreg(host, host->id, SDIO_CLOCK_CTRL2_REG_OFS);
|
|
clock_ctrl.bit.OUTDLY_SEL = outdly_sel;
|
|
sdiohost_setreg(host, host->id, SDIO_CLOCK_CTRL2_REG_OFS, clock_ctrl.reg);
|
|
}
|
|
|
|
void sdiohost_setphyclkindly(struct mmc_nvt_host *host, uint32_t indly_sel)
|
|
{
|
|
union SDIO_CLOCK_CTRL2_REG clock_ctrl;
|
|
|
|
clock_ctrl.reg = sdiohost_getreg(host, host->id, SDIO_CLOCK_CTRL2_REG_OFS);
|
|
clock_ctrl.bit.INDLY_SEL = indly_sel;
|
|
sdiohost_setreg(host, host->id, SDIO_CLOCK_CTRL2_REG_OFS, clock_ctrl.reg);
|
|
}
|
|
|
|
static u32 driving_xfer(u32 driving)
|
|
{
|
|
u32 pad_driving;
|
|
|
|
if (driving == 40)
|
|
pad_driving = PAD_DRIVINGSINK_40MA;
|
|
else if (driving == 10)
|
|
pad_driving = PAD_DRIVINGSINK_10MA;
|
|
else if (driving == 15)
|
|
pad_driving = PAD_DRIVINGSINK_15MA;
|
|
else if (driving == 20)
|
|
pad_driving = PAD_DRIVINGSINK_20MA;
|
|
else if (driving == 25)
|
|
pad_driving = PAD_DRIVINGSINK_25MA;
|
|
else if (driving == 30)
|
|
pad_driving = PAD_DRIVINGSINK_30MA;
|
|
else if (driving == 35)
|
|
pad_driving = PAD_DRIVINGSINK_35MA;
|
|
else if (driving == 6)
|
|
pad_driving = PAD_DRIVINGSINK_6MA;
|
|
else if (driving == 16)
|
|
pad_driving = PAD_DRIVINGSINK_16MA;
|
|
else if (driving == 4)
|
|
pad_driving = PAD_DRIVINGSINK_4MA;
|
|
else if (driving == 8)
|
|
pad_driving = PAD_DRIVINGSINK_8MA;
|
|
else if (driving == 12)
|
|
pad_driving = PAD_DRIVINGSINK_12MA;
|
|
else if (driving == 3)
|
|
pad_driving = PAD_DRIVINGSINK_3MA;
|
|
else if (driving == 9)
|
|
pad_driving = PAD_DRIVINGSINK_9MA;
|
|
else if (driving == 18)
|
|
pad_driving = PAD_DRIVINGSINK_18MA;
|
|
else if (driving == 21)
|
|
pad_driving = PAD_DRIVINGSINK_21MA;
|
|
else if (driving == 24)
|
|
pad_driving = PAD_DRIVINGSINK_24MA;
|
|
else
|
|
pad_driving = PAD_DRIVINGSINK_5MA;
|
|
|
|
return pad_driving;
|
|
}
|
|
|
|
/*
|
|
Set PAD drive/sink of clock pin for specified SDIO channel.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] driving desired driving value * 10, unit: mA
|
|
valid value: 50 ~ 200
|
|
|
|
@return
|
|
- @b E_OK: sucess
|
|
- @b Else: fail
|
|
*/
|
|
int sdiohost_setpaddriving(struct mmc_nvt_host *host, SDIO_SPEED_MODE mode)
|
|
{
|
|
uint32_t data_uidriving, cmd_uidriving, clk_uidriving;
|
|
|
|
if (host->id >= SDIO_HOST_ID_COUNT)
|
|
return E_SYS;
|
|
|
|
if (mode == SDIO_MODE_DS) {
|
|
data_uidriving = driving_xfer(host->pad_driving[SDIO_DS_MODE_DATA]);
|
|
cmd_uidriving = driving_xfer(host->pad_driving[SDIO_DS_MODE_CMD]);
|
|
clk_uidriving = driving_xfer(host->pad_driving[SDIO_DS_MODE_CLK]);
|
|
} else if (mode == SDIO_MODE_HS) {
|
|
data_uidriving = driving_xfer(host->pad_driving[SDIO_HS_MODE_DATA]);
|
|
cmd_uidriving = driving_xfer(host->pad_driving[SDIO_HS_MODE_CMD]);
|
|
clk_uidriving = driving_xfer(host->pad_driving[SDIO_HS_MODE_CLK]);
|
|
} else if (mode == SDIO_MODE_SDR50) {
|
|
data_uidriving = driving_xfer(host->pad_driving[SDIO_SDR50_MODE_DATA]);
|
|
cmd_uidriving = driving_xfer(host->pad_driving[SDIO_SDR50_MODE_CMD]);
|
|
clk_uidriving = driving_xfer(host->pad_driving[SDIO_SDR50_MODE_CLK]);
|
|
} else {
|
|
data_uidriving = driving_xfer(host->pad_driving[SDIO_SDR104_MODE_DATA]);
|
|
cmd_uidriving = driving_xfer(host->pad_driving[SDIO_SDR104_MODE_CMD]);
|
|
clk_uidriving = driving_xfer(host->pad_driving[SDIO_SDR104_MODE_CLK]);
|
|
}
|
|
|
|
if (host->id == SDIO_HOST_ID_1) {
|
|
pad_set_drivingsink(PAD_DS_CGPIO12, cmd_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO13, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO14, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO15, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO16, data_uidriving);
|
|
return pad_set_drivingsink(PAD_DS_CGPIO11, clk_uidriving);
|
|
} else if (host->id == SDIO_HOST_ID_2) {
|
|
pad_set_drivingsink(PAD_DS_CGPIO18, cmd_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO19, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO20, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO21, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO22, data_uidriving);
|
|
return pad_set_drivingsink(PAD_DS_CGPIO17, clk_uidriving);
|
|
} else {
|
|
if (host->mmc) {
|
|
if (host->mmc->caps & MMC_CAP_8_BIT_DATA) {
|
|
pad_set_drivingsink(PAD_DS_CGPIO0, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO1, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO2, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO3, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO4, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO5, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO6, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO7, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO9, cmd_uidriving);
|
|
return pad_set_drivingsink(PAD_DS_CGPIO8, clk_uidriving);
|
|
} else {
|
|
pad_set_drivingsink(PAD_DS_CGPIO0, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO1, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO2, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO3, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO9, cmd_uidriving);
|
|
return pad_set_drivingsink(PAD_DS_CGPIO8, clk_uidriving);
|
|
}
|
|
} else {
|
|
pad_set_drivingsink(PAD_DS_CGPIO0, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO1, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO2, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO3, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO4, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO5, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO6, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO7, data_uidriving);
|
|
pad_set_drivingsink(PAD_DS_CGPIO9, cmd_uidriving);
|
|
return pad_set_drivingsink(PAD_DS_CGPIO8, clk_uidriving);
|
|
}
|
|
}
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
/*
|
|
Reset SDIO host controller.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_reset(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_CMD_REG cmdreg;
|
|
int i = 0;
|
|
|
|
cmdreg.reg = sdiohost_getreg(host, id, SDIO_CMD_REG_OFS);
|
|
cmdreg.bit.SDC_RST = 1;
|
|
sdiohost_setreg(host, id, SDIO_CMD_REG_OFS, cmdreg.reg);
|
|
|
|
while (1) {
|
|
cmdreg.reg = sdiohost_getreg(host, id, SDIO_CMD_REG_OFS);
|
|
|
|
if ((cmdreg.bit.SDC_RST == 0) || (i == RESET_TIMEOUT))
|
|
break;
|
|
|
|
i++;
|
|
}
|
|
|
|
if (i == RESET_TIMEOUT)
|
|
pr_err("sdc reset timeout\n");
|
|
}
|
|
|
|
/*
|
|
Asynchrous reset SDIO host controller.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
- @b SDIO_HOST_ID_3: SDIO3
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_resetasync(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_DLY1_REG dlyreg1;
|
|
union SDIO_PHY_REG phyreg;
|
|
union SDIO_PHY_REG phyregread;
|
|
union SDIO_FIFO_SWITCH_REG fifoswitch;
|
|
|
|
clk_disable_unprepare(host->clk);
|
|
clk_prepare_enable(host->clk);
|
|
|
|
/* patch begin for sd write hang-up or write byte access error */
|
|
fifoswitch.reg = sdiohost_getreg(host, id, SDIO_FIFO_SWITCH_REG_OFS);
|
|
fifoswitch.bit.FIFO_SWITCH_DLY = 1;
|
|
sdiohost_setreg(host, id, SDIO_FIFO_SWITCH_REG_OFS, fifoswitch.reg);
|
|
/* patch end for sd write hang-up or write byte access error */
|
|
|
|
phyreg.reg = sdiohost_getreg(host, id, SDIO_PHY_REG_OFS);
|
|
phyreg.bit.PHY_SW_RST = 1;
|
|
sdiohost_setreg(host, id, SDIO_PHY_REG_OFS, phyreg.reg);
|
|
|
|
while (1) {
|
|
phyregread.reg = sdiohost_getreg(host, id, SDIO_PHY_REG_OFS);
|
|
if (phyregread.bit.PHY_SW_RST == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
dlyreg1.reg = sdiohost_getreg(host, id, SDIO_DLY1_REG_OFS);
|
|
dlyreg1.bit.DATA_READ_DLY = 2;
|
|
sdiohost_setreg(host, id, SDIO_DLY1_REG_OFS, dlyreg1.reg);
|
|
}
|
|
|
|
/*
|
|
Enable FIFO 512 Bytes (Only SDIO3).
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_3: SDIO3
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_setblkfifoen(struct mmc_nvt_host *host, bool enable)
|
|
{
|
|
union SDIO_PHY_REG phyreg;
|
|
|
|
phyreg.reg = sdiohost_getreg(host, host->id, SDIO_PHY_REG_OFS);
|
|
phyreg.bit.BLK_FIFO_EN = enable;
|
|
sdiohost_setreg(host, host->id, SDIO_PHY_REG_OFS, phyreg.reg);
|
|
}
|
|
|
|
/*
|
|
Send SD command to SD bus.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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.
|
|
*/
|
|
int sdiohost_sendcmd(struct mmc_nvt_host *host, uint32_t id, uint32_t cmd,
|
|
SDIO_HOST_RESPONSE rsptype, bool beniointdetect)
|
|
{
|
|
|
|
union SDIO_CMD_REG cmdreg;
|
|
#if (!SDIO_SCATTER_DMA)
|
|
/* Temp solution for FPGA */
|
|
sdiohost_setreg(host, id, 0x1FC, 1);
|
|
#endif
|
|
/*cmdreg.reg = 0;*/
|
|
cmdreg.reg = sdiohost_getreg(host, id, 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, id, SDIO_CMD_REG_OFS, cmdreg.reg);
|
|
|
|
if (id >= SDIO_HOST_ID_COUNT)
|
|
return SDIO_HOST_CMD_FAIL;
|
|
|
|
/* clear software status */
|
|
/*clr_flg(FLG_ID_SDIO, vsdiohosts[id].flgCommand);*/
|
|
|
|
/* Start command/data transmits */
|
|
cmdreg.bit.CMD_EN = 1;
|
|
sdiohost_setreg(host, id, SDIO_CMD_REG_OFS, cmdreg.reg);
|
|
|
|
return SDIO_HOST_CMD_OK;
|
|
}
|
|
|
|
/*
|
|
Set SDIO command argument.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] arg command argument
|
|
- possible value: 0x0000_0000 ~ 0xFFFF_FFFF
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_setarg(struct mmc_nvt_host *host, uint32_t id, uint32_t arg)
|
|
{
|
|
sdiohost_setreg(host, id, SDIO_ARGU_REG_OFS, arg);
|
|
}
|
|
|
|
/*
|
|
Get SDIO command response.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[out] prsp command response from card
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_getshortrsp(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t *prsp)
|
|
{
|
|
union SDIO_RSP0_REG rspreg;
|
|
|
|
rspreg.reg = sdiohost_getreg(host, id, SDIO_RSP0_REG_OFS);
|
|
*prsp = (uint32_t) rspreg.reg;
|
|
}
|
|
|
|
/*
|
|
Get SDIO command long response.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[out] prsp3 command response from card (bit 127..96)
|
|
@param[out] prsp2 command response from card (bit 95..64)
|
|
@param[out] prsp1 command response from card (bit 63..32)
|
|
@param[out] prsp0 command response from card (bit 31..0)
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_getlongrsp(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t *prsp3, uint32_t *prsp2, uint32_t *prsp1, uint32_t *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, id, SDIO_RSP0_REG_OFS);
|
|
*prsp0 = (uint32_t) rsp0reg.reg;
|
|
rsp1reg.reg = sdiohost_getreg(host, id, SDIO_RSP1_REG_OFS);
|
|
*prsp1 = (uint32_t) rsp1reg.reg;
|
|
rsp2reg.reg = sdiohost_getreg(host, id, SDIO_RSP2_REG_OFS);
|
|
*prsp2 = (uint32_t) rsp2reg.reg;
|
|
rsp3reg.reg = sdiohost_getreg(host, id, SDIO_RSP3_REG_OFS);
|
|
*prsp3 = (uint32_t) rsp3reg.reg;
|
|
}
|
|
|
|
/*
|
|
Enable SDIO interrupt.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] bits SDIO controller interrupt enable bits
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_setinten(struct mmc_nvt_host *host, uint32_t id, uint32_t bits)
|
|
{
|
|
union SDIO_INT_MASK_REG intenreg;
|
|
|
|
intenreg.reg = sdiohost_getreg(host, id, SDIO_INT_MASK_REG_OFS);
|
|
intenreg.reg |= bits;
|
|
sdiohost_setreg(host, id, SDIO_INT_MASK_REG_OFS, intenreg.reg);
|
|
}
|
|
|
|
/*
|
|
Disable SDIO interrupt.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] bits SDIO controller interrupt enable bits
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_disinten(struct mmc_nvt_host *host, uint32_t id, uint32_t bits)
|
|
{
|
|
union SDIO_INT_MASK_REG intenreg;
|
|
|
|
intenreg.reg = sdiohost_getreg(host, id, SDIO_INT_MASK_REG_OFS);
|
|
intenreg.reg &= ~bits;
|
|
sdiohost_setreg(host, id, SDIO_INT_MASK_REG_OFS, intenreg.reg);
|
|
}
|
|
|
|
/*
|
|
Set SDIO bus width.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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, uint32_t id,
|
|
uint32_t width)
|
|
{
|
|
union SDIO_BUS_WIDTH_REG widthreg;
|
|
|
|
widthreg.reg = 0;
|
|
widthreg.bit.BUS_WIDTH = width;
|
|
sdiohost_setreg(host, id, SDIO_BUS_WIDTH_REG_OFS, widthreg.reg);
|
|
}
|
|
|
|
/*
|
|
Get SDIO bus width.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return SDIO controller bus Width
|
|
- @b SDIO_BUS_WIDTH1
|
|
- @b SDIO_BUS_WIDTH4
|
|
- @b SDIO_BUS_WIDTH8
|
|
*/
|
|
uint32_t sdiohost_getbuswidth(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_BUS_WIDTH_REG widthreg;
|
|
|
|
widthreg.reg = sdiohost_getreg(host, id, SDIO_BUS_WIDTH_REG_OFS);
|
|
|
|
return widthreg.bit.BUS_WIDTH;
|
|
}
|
|
|
|
/*
|
|
Set SDIO bus clock.
|
|
|
|
@param[in] id SD host ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] uiclock SD bus clock in Hz
|
|
@param[out] ns ns one cycle
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_setbusclk(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t uiclock, uint32_t *ns)
|
|
{
|
|
clk_set_rate(host->clk, uiclock);
|
|
|
|
if (ns)
|
|
*ns = (1000000) / (uiclock/1000);
|
|
}
|
|
|
|
/*
|
|
Get SDIO bus clock.
|
|
|
|
@param[in] id SD host ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return unit of Hz
|
|
*/
|
|
uint32_t sdiohost_getbusclk(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
return clk_get_rate(host->clk);
|
|
}
|
|
|
|
/*
|
|
Get SDIO controller block size.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return data size per block (unit: byte) (0x0001 ~ 0xFFFF)
|
|
*/
|
|
uint32_t sdiohost_getblksize(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_DATA_CTRL_REG datactrlreg;
|
|
|
|
datactrlreg.reg = sdiohost_getreg(host, id, SDIO_DATA_CTRL_REG_OFS);
|
|
return datactrlreg.bit.BLK_SIZE;
|
|
}
|
|
|
|
/*
|
|
Set SDIO controller block size.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] size data size per block (unit: byte)
|
|
- possible value: 0x0001 ~ 0xFFFF
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_setblksize(struct mmc_nvt_host *host, uint32_t id, uint32_t size)
|
|
{
|
|
union SDIO_DATA_CTRL_REG datactrlreg;
|
|
|
|
datactrlreg.reg = sdiohost_getreg(host, id, SDIO_DATA_CTRL_REG_OFS);
|
|
datactrlreg.bit.BLK_SIZE = size;
|
|
sdiohost_setreg(host, id, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg);
|
|
}
|
|
|
|
/*
|
|
Set SDIO controller data timeout.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] timeout time out value between data blocks (unit: SD clock)
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_setdatatimeout(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t timeout)
|
|
{
|
|
union SDIO_DATA_TIMER_REG timerreg;
|
|
|
|
timerreg.bit.TIMEOUT = timeout;
|
|
sdiohost_setreg(host, id, SDIO_DATA_TIMER_REG_OFS, timerreg.reg);
|
|
}
|
|
|
|
/*
|
|
Reset SDIO controller data state machine.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return void
|
|
*/
|
|
void sdiohost_resetdata(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_DATA_CTRL_REG datactrlreg;
|
|
union SDIO_FIFO_CONTROL_REG fifoctrlreg;
|
|
/* //#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, id, SDIO_STATUS_REG_OFS, stsreg.reg);
|
|
|
|
|
|
fifoctrlreg.reg = 0;
|
|
sdiohost_setreg(host, id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
|
|
while (1) {
|
|
fifoctrlreg.reg = sdiohost_getreg(host, id,
|
|
SDIO_FIFO_CONTROL_REG_OFS);
|
|
if (fifoctrlreg.bit.FIFO_EN == 0)
|
|
break;
|
|
}
|
|
|
|
datactrlreg.reg = sdiohost_getreg(host, id, SDIO_DATA_CTRL_REG_OFS);
|
|
datactrlreg.bit.DATA_EN = 0;
|
|
sdiohost_setreg(host, id, 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, id);
|
|
}
|
|
|
|
void sdiohost_delayd(struct mmc_nvt_host *host, uint32_t uid)
|
|
{
|
|
uint32_t i, uidummy;
|
|
|
|
for (i = uid; i; i--)
|
|
uidummy = sdiohost_getreg(host, 0, SDIO_CMD_REG_OFS);
|
|
|
|
}
|
|
|
|
void sdiohost_waitfifoempty(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_FIFO_STATUS_REG fifostsreg;
|
|
uint32_t read0, read1;
|
|
|
|
read0 = 0;
|
|
while (1) {
|
|
fifostsreg.reg = sdiohost_getreg(host, id,
|
|
SDIO_FIFO_STATUS_REG_OFS);
|
|
read1 = fifostsreg.bit.FIFO_EMPTY;
|
|
if (read0 & read1)
|
|
break;
|
|
else
|
|
read0 = read1;
|
|
}
|
|
}
|
|
void sdiohost_clrfifoen(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_FIFO_CONTROL_REG fifoctrlreg;
|
|
|
|
fifoctrlreg.reg = sdiohost_getreg(host, id, SDIO_FIFO_CONTROL_REG_OFS);
|
|
fifoctrlreg.bit.FIFO_EN = 0;
|
|
sdiohost_setreg(host, id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
}
|
|
uint32_t sdiohost_getfifodir(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_FIFO_CONTROL_REG fifoctrlreg;
|
|
|
|
fifoctrlreg.reg = sdiohost_getreg(host, id, SDIO_FIFO_CONTROL_REG_OFS);
|
|
|
|
return fifoctrlreg.bit.FIFO_DIR;
|
|
}
|
|
|
|
/*
|
|
Setup SDIO controller data transfer in DMA mode.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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, uint32_t id,
|
|
uint32_t uidmaaddress, uint32_t uidatalength, bool bisread, uint32_t *vuisdio_destab)
|
|
{
|
|
union SDIO_DATA_CTRL_REG datactrlreg;
|
|
union SDIO_DATA_LENGTH_REG datalenreg;
|
|
union SDIO_FIFO_CONTROL_REG fifoctrlreg;
|
|
#if (SDIO_SCATTER_DMA)
|
|
union SDIO_DMA_DES_START_ADDR_REG dmadesaddrreg;
|
|
union SDIO_DES_LINE_REG dmadeslinereg;
|
|
union SDIO_DES_LINE1_REG dmadesline1reg;
|
|
union SDIO_DES_LINE2_REG dmadesline2reg;
|
|
/*uint32_t i;*/
|
|
#else
|
|
union SDIO_DMA_START_ADDR_REG dmaaddrreg;
|
|
#endif
|
|
uint32_t uibusclk;
|
|
|
|
|
|
/* for debug */
|
|
/*fifoctrlreg.reg = sdiohost_getreg(id, SDIO_FIFO_CONTROL_REG_OFS);
|
|
if (fifoctrlreg.bit.FIFO_EN == 1) {
|
|
fifoctrlreg.reg = 0;
|
|
sdiohost_setreg(id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
}*/
|
|
/* for debug end */
|
|
|
|
/* 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, id, SDIO_DATA_CTRL_REG_OFS);
|
|
|
|
#if 0
|
|
/* 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;
|
|
#else
|
|
/*
|
|
* The DIS_SDIO_INT_PERIOD is designed for reading infinite blocks.
|
|
* But there are some compatibility and performance issues, so it should not be used.
|
|
*/
|
|
datactrlreg.bit.DIS_SDIO_INT_PERIOD = 0;
|
|
#endif
|
|
|
|
/*move data en after fifo en*/
|
|
/*datactrlreg.bit.DATA_EN = 1;*/
|
|
sdiohost_setreg(host, id, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg);
|
|
|
|
#if (SDIO_SCATTER_DMA)
|
|
if (vsdio_seg_en[id] == FALSE) {
|
|
dmadeslinereg.reg = 0;
|
|
dmadesline1reg.reg = 0;
|
|
dmadesline2reg.reg = 0;
|
|
|
|
dmadeslinereg.bit.DMA_DES_VALID = 1;
|
|
dmadeslinereg.bit.DMA_DES_END = 1;
|
|
dmadeslinereg.bit.DMA_DES_ACT = 2;
|
|
|
|
dmadesline1reg.bit.DMA_DES_DATA_LEN = uidatalength;
|
|
dmadesline2reg.bit.DMA_DES_DATA_ADDR = uidmaaddress;
|
|
|
|
vuisdio_destab[SDIO_DES_LINE_REG_OFS] =
|
|
(uint32_t)dmadeslinereg.reg;
|
|
vuisdio_destab[SDIO_DES_LINE1_REG_OFS>>2] =
|
|
(uint32_t)dmadesline1reg.reg;
|
|
vuisdio_destab[SDIO_DES_LINE2_REG_OFS>>2] =
|
|
(uint32_t)dmadesline2reg.reg;
|
|
}
|
|
|
|
dmadesaddrreg.reg = 0;
|
|
dmadesaddrreg.bit.DES_ADDR = virt_to_phys((uint32_t*)&vuisdio_destab[0]);
|
|
sdiohost_setreg(host, id, SDIO_DMA_DES_START_ADDR_REG_OFS,
|
|
dmadesaddrreg.reg);
|
|
|
|
datalenreg.reg = 0;
|
|
datalenreg.bit.LENGTH = uidatalength;
|
|
sdiohost_setreg(host, id, SDIO_DATA_LENGTH_REG_OFS, datalenreg.reg);
|
|
|
|
fifoctrlreg.reg = 0;
|
|
|
|
if (vsdio_seg_en[id] == FALSE) {
|
|
/* Flush cache in data DMA*/
|
|
if (!bisread) {
|
|
dma_flushwritecache(mmc_dev(host->mmc), (void *)uidmaaddress, uidatalength);
|
|
fifoctrlreg.bit.FIFO_DIR = 1;
|
|
} else {
|
|
dma_flushreadcache(mmc_dev(host->mmc), (void *)uidmaaddress, uidatalength);
|
|
}
|
|
|
|
/* Flush cache in Des*/
|
|
dma_flushbidircache(mmc_dev(host->mmc), (void *)&vuisdio_destab[0],
|
|
SDIO_DES_WORD_SIZE<<2);
|
|
} else {
|
|
#if 0
|
|
for (i = 0; i < vsdio_seg_num[id]; i++) {
|
|
/* Flush cache in data DMA*/
|
|
if (!bisread) {
|
|
dma_flushwritecache(mmc_dev(host->mmc), (void *)(mmc_dev(host->mmc), (vuisdio_destab
|
|
[(i * SDIO_DES_WORD_SIZE) +
|
|
(SDIO_DES_LINE2_REG_OFS>>2)])),
|
|
vuisdio_destab[(i * SDIO_DES_WORD_SIZE +
|
|
SDIO_DES_LINE1_REG_OFS)>>2]);
|
|
fifoctrlreg.bit.FIFO_DIR = 1;
|
|
} else {
|
|
dma_flushreadcache(mmc_dev(host->mmc), (void *)(mmc_dev(host->mmc), (vuisdio_destab
|
|
[(i * SDIO_DES_WORD_SIZE) +
|
|
(SDIO_DES_LINE2_REG_OFS>>2)])),
|
|
vuisdio_destab[(i * SDIO_DES_WORD_SIZE +
|
|
SDIO_DES_LINE1_REG_OFS)>>2]);
|
|
}
|
|
|
|
}
|
|
#else
|
|
if (!bisread)
|
|
fifoctrlreg.bit.FIFO_DIR = 1;
|
|
else
|
|
fifoctrlreg.bit.FIFO_DIR = 0;
|
|
#endif
|
|
|
|
/* Flush cache in Des*/
|
|
dma_flushbidircache(mmc_dev(host->mmc), (void *)(&vuisdio_destab[0]),
|
|
(SDIO_DES_WORD_SIZE<<2)*SDIO_DES_TABLE_NUM);
|
|
}
|
|
#else
|
|
dmaaddrreg.reg = 0;
|
|
dmaaddrreg.bit.DRAM_ADDR = virt_to_phys((uint32_t *)uidmaaddress);
|
|
sdiohost_setreg(host, id, SDIO_DMA_START_ADDR_REG_OFS, dmaaddrreg.reg);
|
|
|
|
datalenreg.reg = 0;
|
|
datalenreg.bit.LENGTH = uidatalength;
|
|
sdiohost_setreg(host, id, SDIO_DATA_LENGTH_REG_OFS, datalenreg.reg);
|
|
|
|
fifoctrlreg.reg = 0;
|
|
|
|
/* Flush cache in DMA mode*/
|
|
if (!bisread) {
|
|
dma_flushwritecache(mmc_dev(host->mmc), (void *)uidmaaddress, uidatalength);
|
|
fifoctrlreg.bit.FIFO_DIR = 1;
|
|
} else {
|
|
dma_flushreadcache(mmc_dev(host->mmc), (void *)uidmaaddress, uidatalength);
|
|
}
|
|
#endif
|
|
|
|
fifoctrlreg.bit.FIFO_MODE = 1;
|
|
sdiohost_setreg(host, id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
|
|
datactrlreg.reg = sdiohost_getreg(host, id, SDIO_DATA_CTRL_REG_OFS);
|
|
datactrlreg.bit.DATA_EN = 1;
|
|
sdiohost_setreg(host, id, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg);
|
|
|
|
fifoctrlreg.bit.FIFO_EN = 1;
|
|
sdiohost_setreg(host, id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
}
|
|
|
|
/*
|
|
Setup SDIO controller data transfer in PIO mode.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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_setupdatatransferpio(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t uidmaaddress, uint32_t uidatalength, bool bisread)
|
|
{
|
|
union SDIO_DATA_CTRL_REG datactrlreg;
|
|
union SDIO_DATA_LENGTH_REG datalenreg;
|
|
union SDIO_FIFO_CONTROL_REG fifoctrlreg;
|
|
#if (!SDIO_SCATTER_DMA)
|
|
union SDIO_DMA_START_ADDR_REG dmaaddrreg;
|
|
#endif
|
|
uint32_t 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, id, SDIO_DATA_CTRL_REG_OFS);
|
|
datactrlreg.bit.DIS_SDIO_INT_PERIOD = 0;
|
|
|
|
datactrlreg.bit.DATA_EN = 1;
|
|
sdiohost_setreg(host, id, SDIO_DATA_CTRL_REG_OFS, datactrlreg.reg);
|
|
|
|
|
|
#if (!SDIO_SCATTER_DMA)
|
|
dmaaddrreg.reg = 0;
|
|
sdiohost_setreg(host, id, SDIO_DMA_START_ADDR_REG_OFS, dmaaddrreg.reg);
|
|
#endif
|
|
|
|
datalenreg.reg = 0;
|
|
datalenreg.bit.LENGTH = uidatalength;
|
|
sdiohost_setreg(host, id, SDIO_DATA_LENGTH_REG_OFS, datalenreg.reg);
|
|
|
|
fifoctrlreg.reg = 0;
|
|
|
|
if (!bisread)
|
|
fifoctrlreg.bit.FIFO_DIR = 1;
|
|
|
|
sdiohost_setreg(host, id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
|
|
fifoctrlreg.bit.FIFO_EN = 1;
|
|
sdiohost_setreg(host, id, SDIO_FIFO_CONTROL_REG_OFS, fifoctrlreg.reg);
|
|
}
|
|
|
|
/*
|
|
Write SDIO data blocks.
|
|
|
|
|
|
@note This function should only be called in PIO mode.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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
|
|
*/
|
|
int sdiohost_writeblock(struct mmc_nvt_host *host, uint32_t id, uint8_t *pbuf,
|
|
uint32_t uilength)
|
|
{
|
|
uint32_t uiwordcount, i, *pbufword;
|
|
uint32_t uifullcount, uiremaincount;
|
|
bool bwordalignment;
|
|
union SDIO_DATA_PORT_REG datareg;
|
|
union SDIO_FIFO_STATUS_REG fifostsreg;
|
|
uint32_t dstatus;
|
|
|
|
uiwordcount = (uilength + sizeof(uint32_t) - 1) / sizeof(uint32_t);
|
|
uifullcount = uiwordcount / SDIO_HOST_DATA_FIFO_DEPTH;
|
|
uiremaincount = uiwordcount % SDIO_HOST_DATA_FIFO_DEPTH;
|
|
pbufword = (uint32_t *)pbuf;
|
|
|
|
if ((uint32_t)pbuf & 0x3)
|
|
bwordalignment = FALSE;
|
|
else
|
|
bwordalignment = TRUE;
|
|
|
|
while (uifullcount) {
|
|
fifostsreg.reg = sdiohost_getreg(host, id,
|
|
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, id,
|
|
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, id,
|
|
SDIO_DATA_PORT_REG_OFS, datareg.reg);
|
|
}
|
|
}
|
|
|
|
uifullcount--;
|
|
}
|
|
|
|
dstatus = sdiohost_getstatus(host, id);
|
|
if ((dstatus & SDIO_STATUS_REG_DATA_CRC_FAIL) || (dstatus & SDIO_STATUS_REG_DATA_TIMEOUT)) {
|
|
return E_SYS;
|
|
}
|
|
}
|
|
|
|
if (uiremaincount) {
|
|
while (1) {
|
|
fifostsreg.reg = sdiohost_getreg(host, id,
|
|
SDIO_FIFO_STATUS_REG_OFS);
|
|
|
|
if (fifostsreg.bit.FIFO_EMPTY)
|
|
break;
|
|
|
|
dstatus = sdiohost_getstatus(host, id);
|
|
if ((dstatus & SDIO_STATUS_REG_DATA_CRC_FAIL) || (dstatus & SDIO_STATUS_REG_DATA_TIMEOUT)) {
|
|
return E_SYS;
|
|
}
|
|
}
|
|
|
|
if (bwordalignment == TRUE) {
|
|
/* Word alignment*/
|
|
for (i = uiremaincount; i; i--) {
|
|
sdiohost_setreg(host, id,
|
|
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, id,
|
|
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[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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
|
|
*/
|
|
int sdiohost_readblock(struct mmc_nvt_host *host, uint32_t id, uint8_t *pbuf,
|
|
uint32_t uilength)
|
|
{
|
|
uint32_t uiwordcount, i, *pbufword;
|
|
uint32_t uifullcount, uiremaincount;
|
|
bool bwordalignment;
|
|
union SDIO_DATA_PORT_REG datareg;
|
|
union SDIO_FIFO_STATUS_REG fifostsreg;
|
|
uint32_t dstatus;
|
|
|
|
uiwordcount = (uilength + sizeof(uint32_t) - 1) / sizeof(uint32_t);
|
|
uifullcount = uiwordcount / SDIO_HOST_DATA_FIFO_DEPTH;
|
|
uiremaincount = uiwordcount % SDIO_HOST_DATA_FIFO_DEPTH;
|
|
pbufword = (uint32_t *)pbuf;
|
|
|
|
if ((uint32_t)pbuf & 0x3)
|
|
bwordalignment = FALSE;
|
|
else
|
|
bwordalignment = TRUE;
|
|
|
|
while (uifullcount) {
|
|
fifostsreg.reg = sdiohost_getreg(host, id,
|
|
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, id,
|
|
SDIO_DATA_PORT_REG_OFS);
|
|
}
|
|
} else {
|
|
/* Not word alignment*/
|
|
for (i = SDIO_HOST_DATA_FIFO_DEPTH; i; i--) {
|
|
datareg.reg = sdiohost_getreg(host, id,
|
|
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--;
|
|
}
|
|
|
|
dstatus = sdiohost_getstatus(host, id);
|
|
if ((dstatus & SDIO_STATUS_REG_DATA_CRC_FAIL) || (dstatus & SDIO_STATUS_REG_DATA_TIMEOUT)) {
|
|
return E_SYS;
|
|
}
|
|
}
|
|
|
|
if (uiremaincount) {
|
|
while (1) {
|
|
fifostsreg.reg = sdiohost_getreg(host, id,
|
|
SDIO_FIFO_STATUS_REG_OFS);
|
|
|
|
if (fifostsreg.bit.FIFO_CNT == uiremaincount)
|
|
break;
|
|
|
|
dstatus = sdiohost_getstatus(host, id);
|
|
if ((dstatus & SDIO_STATUS_REG_DATA_CRC_FAIL) || (dstatus & SDIO_STATUS_REG_DATA_TIMEOUT)) {
|
|
return E_SYS;
|
|
}
|
|
}
|
|
|
|
if (bwordalignment == TRUE) {
|
|
/* Word alignment*/
|
|
for (i = uiremaincount; i; i--) {
|
|
*pbufword++ = sdiohost_getreg(host, id,
|
|
SDIO_DATA_PORT_REG_OFS);
|
|
}
|
|
} else {
|
|
/* Not word alignment*/
|
|
for (i = uiremaincount; i; i--) {
|
|
datareg.reg = sdiohost_getreg(host, id,
|
|
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;
|
|
}
|
|
|
|
uint32_t sdiohost_setiointen(struct mmc_nvt_host *host, uint32_t id, bool ben)
|
|
{
|
|
union SDIO_INT_MASK_REG intenreg;
|
|
|
|
intenreg.reg = sdiohost_getreg(host, id, SDIO_INT_MASK_REG_OFS);
|
|
intenreg.bit.SDIO_INT_INT_EN = ben;
|
|
sdiohost_setreg(host, id, SDIO_INT_MASK_REG_OFS, intenreg.reg);
|
|
return TRUE;
|
|
}
|
|
|
|
bool sdiohost_getiointen(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_INT_MASK_REG intenreg;
|
|
|
|
intenreg.reg = sdiohost_getreg(host, id, SDIO_INT_MASK_REG_OFS);
|
|
if (intenreg.bit.SDIO_INT_INT_EN)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
/** ********************************************************************
|
|
|
|
|
|
Private(in driver layer) SDIO host controller functions
|
|
|
|
|
|
***********************************************************************/
|
|
|
|
#if (SDIO_HOST_ID_COUNT > 1)
|
|
/*
|
|
Get SDIO controller register.
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@param[in] offset register offset in SDIO controller (word alignment)
|
|
|
|
@return register value
|
|
*/
|
|
static REGVALUE sdiohost_getreg(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t offset)
|
|
{
|
|
REGVALUE value;
|
|
|
|
value = nvt_readl(host->base + offset);
|
|
|
|
return value;
|
|
}
|
|
#endif
|
|
|
|
#if (SDIO_HOST_ID_COUNT > 1)
|
|
/*
|
|
Set SDIO controller register.
|
|
|
|
@param[in] id DIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
@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, uint32_t id,
|
|
uint32_t offset, REGVALUE value)
|
|
{
|
|
nvt_writel(value, host->base + offset);
|
|
}
|
|
#endif
|
|
|
|
/*uint32_t Flag = 0;*/
|
|
#include <linux/delay.h>
|
|
|
|
void sdiohost_delay(uint32_t uius)
|
|
{
|
|
udelay(uius);
|
|
}
|
|
|
|
int sdiohost_sendsdcmd(struct mmc_nvt_host *host, uint32_t id, uint32_t cmdpart,
|
|
uint32_t param)
|
|
{
|
|
int status;
|
|
bool benintdetect = FALSE;
|
|
SDIO_HOST_RESPONSE rsptype = SDIO_HOST_RSP_NONE;
|
|
#if 0
|
|
pr_info("CMD%d arg %x %x\r\n",
|
|
cmdpart&(~(SDIO_CMD_REG_APP_CMD | SDIO_CMD_REG_VOLTAGE_SWITCH_DETECT
|
|
| SDIO_CMD_REG_ABORT | SDIO_CMD_REG_NEED_RSP | SDIO_CMD_REG_LONG_RSP
|
|
| SDIO_CMD_REG_RSP_TYPE2)),
|
|
param, cmdpart);
|
|
#endif
|
|
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_setarg(host, id, param);
|
|
/*pr_info("bEnIntDetect %d\r\n",bEnIntDetect);*/
|
|
|
|
status = sdiohost_sendcmd(host, id, cmdpart&SDIO_CMD_REG_INDEX,
|
|
rsptype, benintdetect);
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
int sdiohost_open(struct mmc_nvt_host *host, uint32_t id, int voltage_switch)
|
|
{
|
|
sdiohost_setpaddriving(host, SDIO_MODE_DS);
|
|
|
|
sdiohost_resetdata(host, id);
|
|
|
|
sdiohost_resetasync(host, id);
|
|
|
|
if (nvt_get_chip_id() == CHIP_NA51055) {
|
|
sdiohost_setphysample(host, true, false);
|
|
} else {
|
|
sdiohost_setphysample(host, false, true);
|
|
}
|
|
|
|
/* Pass TG1.1-15: init freq should <=400Khz. */
|
|
/* Set to 399KHz to guarantee pass TG1.1-15 */
|
|
/*sdiohost_setbusclk(id, 399000, &ns);*/
|
|
|
|
/* Delay 1 ms (SD spec) after clock is outputted. */
|
|
/* (Delay 1024 us to reduce code size) */
|
|
sdiohost_delay(1024);
|
|
|
|
/*sdioHost_setClkGating(id, bClkGating);*/
|
|
/* restore clock gating*/
|
|
|
|
/* Interrupt Enable */
|
|
sdiohost_setinten(host, id,
|
|
SDIO_STATUS_REG_DATA_TIMEOUT |
|
|
SDIO_STATUS_REG_DATA_CRC_FAIL|
|
|
SDIO_STATUS_REG_DATA_CRC_OK |
|
|
SDIO_STATUS_REG_DATA_END |
|
|
SDIO_STATUS_REG_RSP_CRC_FAIL |
|
|
SDIO_STATUS_REG_RSP_TIMEOUT |
|
|
SDIO_STATUS_REG_RSP_CRC_OK |
|
|
SDIO_STATUS_REG_CMD_SEND);
|
|
|
|
sdiohost_setdatatimeout(host, id, 0x10000000);
|
|
|
|
sdiohost_enclockout(host, id);
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
|
|
void sdiohost_setstatus(struct mmc_nvt_host *host, uint32_t id,
|
|
uint32_t status)
|
|
{
|
|
sdiohost_setreg(host, id, SDIO_STATUS_REG_OFS, status);
|
|
}
|
|
|
|
uint32_t sdiohost_getstatus(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
return sdiohost_getreg(host, id, SDIO_STATUS_REG_OFS);
|
|
}
|
|
|
|
void sdiohost_setpower(struct mmc_nvt_host *host, SDIO_HOST_SETPOWER_VOL vol)
|
|
{
|
|
PAD_POWER_STRUCT pad_power[1] = {0};
|
|
|
|
if (host->id == SDIO_HOST_ID_2) {
|
|
/*
|
|
pad_power->enable = 1;
|
|
pad_power->pad_power_id = PAD_POWERID_MC1;
|
|
if (vol == SDIO_HOST_SETPOWER_VOL_3P3)
|
|
pad_power->pad_power = PAD_3P3V;
|
|
else if (vol == SDIO_HOST_SETPOWER_VOL_1P8)
|
|
pad_power->pad_power = PAD_1P8V;
|
|
else
|
|
pad_power->enable = 0;
|
|
|
|
pad_set_power(pad_power);
|
|
*/
|
|
} else if (host->id == SDIO_HOST_ID_1) {
|
|
pad_power->enable = 1;
|
|
pad_power->pad_power_id = PAD_POWERID_MC0;
|
|
if (vol == SDIO_HOST_SETPOWER_VOL_3P3)
|
|
pad_power->pad_power = PAD_3P3V;
|
|
else if (vol == SDIO_HOST_SETPOWER_VOL_1P8)
|
|
pad_power->pad_power = PAD_1P8V;
|
|
else
|
|
pad_power->enable = 0;
|
|
|
|
pad_set_power(pad_power);
|
|
}
|
|
}
|
|
|
|
int sdiohost_getpower(struct mmc_nvt_host *host)
|
|
{
|
|
PAD_POWER_STRUCT pad_power[1] = {0};
|
|
|
|
if (host->id == SDIO_HOST_ID_2) {
|
|
/*
|
|
pad_power->pad_power_id = PAD_POWERID_MC1;
|
|
pad_get_power(pad_power);
|
|
|
|
return pad_power->pad_power;
|
|
*/
|
|
return 1;
|
|
} else if (host->id == SDIO_HOST_ID_1) {
|
|
pad_power->pad_power_id = PAD_POWERID_MC0;
|
|
pad_get_power(pad_power);
|
|
|
|
return pad_power->pad_power;
|
|
} else
|
|
return 1;
|
|
}
|
|
|
|
static int sdiohost_getpower_enable(struct mmc_nvt_host *host)
|
|
{
|
|
PAD_POWER_STRUCT pad_power[1] = {0};
|
|
|
|
if (host->id == SDIO_HOST_ID_2) {
|
|
/*
|
|
pad_power->pad_power_id = PAD_POWERID_MC1;
|
|
pad_get_power(pad_power);
|
|
|
|
return pad_power->enable;
|
|
*/
|
|
return 1;
|
|
} else if (host->id == SDIO_HOST_ID_1) {
|
|
pad_power->pad_power_id = PAD_POWERID_MC0;
|
|
pad_get_power(pad_power);
|
|
|
|
return pad_power->enable;
|
|
} else
|
|
return 1;
|
|
}
|
|
|
|
void sdiohost_power_switch(struct mmc_nvt_host *host, bool enable)
|
|
{
|
|
if (enable) {
|
|
if (!sdiohost_getpower_enable(host))
|
|
sdiohost_setpower(host, SDIO_HOST_SETPOWER_VOL_3P3);
|
|
} else
|
|
sdiohost_setpower(host, SDIO_HOST_SETPOWER_VOL_0);
|
|
}
|
|
|
|
void sdiohost_set_pads(struct mmc_nvt_host *host, PAD_PULL pulltype)
|
|
{
|
|
if (host->id == SDIO_HOST_ID_1) {
|
|
pad_set_pull_updown(PAD_PIN_CGPIO12, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO13, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO14, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO15, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO16, pulltype);
|
|
} else if (host->id == SDIO_HOST_ID_2) {
|
|
#if defined(CONFIG_NVT_IVOT_PLAT_NA51089)
|
|
if (host->pinmux_value & PIN_SDIO_CFG_2ND_PINMUX)
|
|
{
|
|
pad_set_pull_updown(PAD_PIN_SGPIO1, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_SGPIO2, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_SGPIO3, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_SGPIO7, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_SGPIO8, pulltype);
|
|
}
|
|
else if (host->pinmux_value & PIN_SDIO_CFG_3RD_PINMUX)
|
|
{
|
|
pad_set_pull_updown(PAD_PIN_DSIGPIO6, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_DSIGPIO2, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_DSIGPIO3, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_DSIGPIO4, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_DSIGPIO5, pulltype);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pad_set_pull_updown(PAD_PIN_CGPIO18, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO19, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO20, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO21, pulltype);
|
|
pad_set_pull_updown(PAD_PIN_CGPIO22, pulltype);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sdiohost_set_gpios(struct mmc_nvt_host *host, bool dir_out, int value)
|
|
{
|
|
if (dir_out) {
|
|
if (host->id == SDIO_HOST_ID_1) {
|
|
gpio_direction_output(C_GPIO(11), value);
|
|
gpio_direction_output(C_GPIO(12), value);
|
|
gpio_direction_output(C_GPIO(13), value);
|
|
gpio_direction_output(C_GPIO(14), value);
|
|
gpio_direction_output(C_GPIO(15), value);
|
|
gpio_direction_output(C_GPIO(16), value);
|
|
} else if (host->id == SDIO_HOST_ID_2) {
|
|
#if defined(CONFIG_NVT_IVOT_PLAT_NA51089)
|
|
if (host->pinmux_value & PIN_SDIO_CFG_2ND_PINMUX)
|
|
{
|
|
gpio_direction_output(S_GPIO(0), value);
|
|
gpio_direction_output(S_GPIO(1), value);
|
|
gpio_direction_output(S_GPIO(2), value);
|
|
gpio_direction_output(S_GPIO(3), value);
|
|
gpio_direction_output(S_GPIO(7), value);
|
|
gpio_direction_output(S_GPIO(8), value);
|
|
}
|
|
else if (host->pinmux_value & PIN_SDIO_CFG_3RD_PINMUX)
|
|
{
|
|
gpio_direction_output(DSI_GPIO(6), value);
|
|
gpio_direction_output(DSI_GPIO(7), value);
|
|
gpio_direction_output(DSI_GPIO(2), value);
|
|
gpio_direction_output(DSI_GPIO(3), value);
|
|
gpio_direction_output(DSI_GPIO(4), value);
|
|
gpio_direction_output(DSI_GPIO(5), value);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
gpio_direction_output(C_GPIO(17), value);
|
|
gpio_direction_output(C_GPIO(18), value);
|
|
gpio_direction_output(C_GPIO(19), value);
|
|
gpio_direction_output(C_GPIO(20), value);
|
|
gpio_direction_output(C_GPIO(21), value);
|
|
gpio_direction_output(C_GPIO(22), value);
|
|
}
|
|
}
|
|
} else {
|
|
if (host->id == SDIO_HOST_ID_1) {
|
|
gpio_direction_input(C_GPIO(11));
|
|
gpio_direction_input(C_GPIO(12));
|
|
gpio_direction_input(C_GPIO(13));
|
|
gpio_direction_input(C_GPIO(14));
|
|
gpio_direction_input(C_GPIO(15));
|
|
gpio_direction_input(C_GPIO(16));
|
|
} else if (host->id == SDIO_HOST_ID_2) {
|
|
#if defined(CONFIG_NVT_IVOT_PLAT_NA51089)
|
|
if (host->pinmux_value & PIN_SDIO_CFG_2ND_PINMUX)
|
|
{
|
|
gpio_direction_input(S_GPIO(0));
|
|
gpio_direction_input(S_GPIO(1));
|
|
gpio_direction_input(S_GPIO(2));
|
|
gpio_direction_input(S_GPIO(3));
|
|
gpio_direction_input(S_GPIO(7));
|
|
gpio_direction_input(S_GPIO(8));
|
|
}
|
|
else if (host->pinmux_value & PIN_SDIO_CFG_3RD_PINMUX)
|
|
{
|
|
gpio_direction_input(DSI_GPIO(6));
|
|
gpio_direction_input(DSI_GPIO(7));
|
|
gpio_direction_input(DSI_GPIO(2));
|
|
gpio_direction_input(DSI_GPIO(3));
|
|
gpio_direction_input(DSI_GPIO(4));
|
|
gpio_direction_input(DSI_GPIO(5));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
gpio_direction_input(C_GPIO(17));
|
|
gpio_direction_input(C_GPIO(18));
|
|
gpio_direction_input(C_GPIO(19));
|
|
gpio_direction_input(C_GPIO(20));
|
|
gpio_direction_input(C_GPIO(21));
|
|
gpio_direction_input(C_GPIO(22));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void sdiohost_power_down(struct mmc_nvt_host *host)
|
|
{
|
|
if (host->id == SDIO_HOST_ID_1) {
|
|
int ret = 0;
|
|
PIN_GROUP_CONFIG pinmux_config[1];
|
|
pinmux_config->pin_function = PIN_FUNC_SDIO;
|
|
pinmux_config->config = 0x0;
|
|
ret = nvt_pinmux_update(pinmux_config, 1);
|
|
if (ret)
|
|
pr_err("Switch SDIO1 pinmux error\n");
|
|
|
|
sdiohost_set_pads(host, PAD_PULLDOWN);
|
|
|
|
sdiohost_set_gpios(host, true, 0);
|
|
} else if (host->id == SDIO_HOST_ID_2) {
|
|
int ret = 0;
|
|
PIN_GROUP_CONFIG pinmux_config[1];
|
|
pinmux_config->pin_function = PIN_FUNC_SDIO2;
|
|
pinmux_config->config = 0x0;
|
|
ret = nvt_pinmux_update(pinmux_config, 1);
|
|
if (ret)
|
|
pr_err("Switch SDIO2 pinmux error\n");
|
|
|
|
sdiohost_set_pads(host, PAD_PULLDOWN);
|
|
|
|
sdiohost_set_gpios(host, true, 0);
|
|
}
|
|
|
|
sdiohost_setpower(host, SDIO_HOST_SETPOWER_VOL_0);
|
|
|
|
if (host->cp_gpio)
|
|
gpio_set_value_cansleep(host->cp_gpio, !host->cp_gpio_value);
|
|
|
|
if (host->power_down_delay_ms > 0) {
|
|
msleep(host->power_down_delay_ms);
|
|
}
|
|
}
|
|
|
|
void sdiohost_power_up(struct mmc_nvt_host *host)
|
|
{
|
|
if (host->cp_gpio)
|
|
gpio_set_value_cansleep(host->cp_gpio, host->cp_gpio_value);
|
|
|
|
if (host->power_up_delay_ms > 0) {
|
|
msleep(host->power_up_delay_ms);
|
|
}
|
|
|
|
sdiohost_setpower(host, SDIO_HOST_SETPOWER_VOL_3P3);
|
|
|
|
if (host->id == SDIO_HOST_ID_1) {
|
|
int ret = 0;
|
|
PIN_GROUP_CONFIG pinmux_config[1];
|
|
|
|
sdiohost_set_gpios(host, false, 0);
|
|
|
|
pinmux_config->pin_function = PIN_FUNC_SDIO;
|
|
pinmux_config->config = host->pinmux_value;
|
|
ret = nvt_pinmux_update(pinmux_config, 1);
|
|
if (ret)
|
|
pr_err("Switch SDIO1 pinmux error\n");
|
|
|
|
sdiohost_set_pads(host, PAD_PULLUP);
|
|
} else if (host->id == SDIO_HOST_ID_2) {
|
|
int ret = 0;
|
|
PIN_GROUP_CONFIG pinmux_config[1];
|
|
|
|
sdiohost_set_gpios(host, false, 0);
|
|
|
|
pinmux_config->pin_function = PIN_FUNC_SDIO2;
|
|
pinmux_config->config = host->pinmux_value;
|
|
ret = nvt_pinmux_update(pinmux_config, 1);
|
|
if (ret)
|
|
pr_err("Switch SDIO2 pinmux error\n");
|
|
|
|
sdiohost_set_pads(host, PAD_PULLUP);
|
|
}
|
|
}
|
|
|
|
|
|
void sdiohost_power_cycle(struct mmc_nvt_host *host, uint32_t delay_ms)
|
|
{
|
|
gpio_set_value_cansleep(host->cp_gpio, !host->cp_gpio_value);
|
|
sdiohost_setpower(host, SDIO_HOST_SETPOWER_VOL_0);
|
|
sdiohost_power_down(host);
|
|
msleep(delay_ms);
|
|
sdiohost_power_up(host);
|
|
sdiohost_setpower(host, SDIO_HOST_SETPOWER_VOL_3P3);
|
|
gpio_set_value_cansleep(host->cp_gpio, host->cp_gpio_value);
|
|
}
|
|
|
|
/*
|
|
Get SDIO Data status
|
|
|
|
@param[in] id SDIO channel ID
|
|
- @b SDIO_HOST_ID_1: SDIO1
|
|
- @b SDIO_HOST_ID_2: SDIO2
|
|
|
|
@return TRUE: ready
|
|
FALSE: busy
|
|
*/
|
|
uint32_t sdiohost_getdata_status(struct mmc_nvt_host *host, uint32_t id)
|
|
{
|
|
union SDIO_BUS_STATUS_REG stsreg;
|
|
|
|
stsreg.reg = sdiohost_getreg(host, id, SDIO_BUS_STATUS_REG_OFS);
|
|
|
|
return stsreg.reg;
|
|
}
|
|
|
|
u32 sdiohost_set_voltage_switch(struct mmc_nvt_host *host)
|
|
{
|
|
union SDIO_DATA_CTRL_REG datactrl_reg;
|
|
union SDIO_VOL_SWITCH_TIMER_REG timer_reg;
|
|
union SDIO_INT_MASK_REG int_mask_reg;
|
|
|
|
host->voltage_switch_timeout = 0;
|
|
|
|
timer_reg.reg = clk_get_rate(host->clk)/1000;
|
|
sdiohost_setreg(host, host->id, SDIO_VOL_SWITCH_TIMER_REG_OFS, timer_reg.reg);
|
|
|
|
int_mask_reg.reg = sdiohost_getreg(host, host->id, SDIO_INT_MASK_REG_OFS);
|
|
int_mask_reg.bit.VOL_SWITCH_END_INT_EN = 1;
|
|
int_mask_reg.bit.VOL_SWITCH_TIMEOUT_INT_EN = 1;
|
|
sdiohost_setreg(host, host->id, SDIO_INT_MASK_REG_OFS, int_mask_reg.reg);
|
|
|
|
datactrl_reg.reg = sdiohost_getreg(host, host->id, SDIO_DATA_CTRL_REG_OFS);
|
|
datactrl_reg.bit.WAIT_VOL_SWITCH_EN = 1;
|
|
sdiohost_setreg(host, host->id, SDIO_DATA_CTRL_REG_OFS, datactrl_reg.reg);
|
|
|
|
wait_for_completion(&host->voltage_switch_complete);
|
|
|
|
int_mask_reg.reg = sdiohost_getreg(host, host->id, SDIO_INT_MASK_REG_OFS);
|
|
int_mask_reg.bit.VOL_SWITCH_END_INT_EN = 0;
|
|
int_mask_reg.bit.VOL_SWITCH_TIMEOUT_INT_EN = 0;
|
|
sdiohost_setreg(host, host->id, SDIO_INT_MASK_REG_OFS, int_mask_reg.reg);
|
|
|
|
if (host->voltage_switch_timeout) {
|
|
pr_err("voltage switch timeout\n");
|
|
if ((host->cp_gpio) && (host->id == SDIO_HOST_ID_1))
|
|
sdiohost_power_cycle(host, 500);
|
|
|
|
return -EIO;
|
|
} else
|
|
return 0;
|
|
|
|
}
|
|
|
|
void sdiohost_setphyphase_cmpen(struct mmc_nvt_host *host, u32 sel)
|
|
{
|
|
}
|
|
|
|
u32 sdiohost_getphydetout(struct mmc_nvt_host *host)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int sdiohost_tuning_cmd(struct mmc_nvt_host *host, u32 opcode, bool print_pattern_err)
|
|
{
|
|
struct STRG_SEG_DES tuning_destable[1];
|
|
struct mmc_data *dummy_data;
|
|
u32 i, blocksize, timeout, status;
|
|
struct platform_device *pdev = to_platform_device(host->mmc->parent);
|
|
u8 *tuningbuf;
|
|
dma_addr_t tuningbuf_pa;
|
|
u8 golden_buf[64] = {
|
|
0xFF, 0x0F, 0xFF, 0x00, 0xFF, 0xCC, 0xC3, 0xCC,
|
|
0xC3, 0x3C, 0xCC, 0xFF, 0xFE, 0xFF, 0xFE, 0xEF,
|
|
0xFF, 0xDF, 0xFF, 0xDD, 0xFF, 0xFB, 0xFF, 0xFB,
|
|
0xBF, 0xFF, 0x7F, 0xFF, 0x77, 0xF7, 0xBD, 0xEF,
|
|
0xFF, 0xF0, 0xFF, 0xF0, 0x0F, 0xFC, 0xCC, 0x3C,
|
|
0xCC, 0x33, 0xCC, 0xCF, 0xFF, 0xEF, 0xFF, 0xEE,
|
|
0xFF, 0xFD, 0xFF, 0xFD, 0xDF, 0xFF, 0xBF, 0xFF,
|
|
0xBB, 0xFF, 0xF7, 0xFF, 0xF7, 0x7F, 0x7B, 0xDE
|
|
};
|
|
|
|
dummy_data = kzalloc(sizeof(struct mmc_data), GFP_KERNEL);
|
|
|
|
if (!dummy_data) {
|
|
pr_err("failed to allocate dummy_data\n");
|
|
goto read_error;
|
|
}
|
|
|
|
host->data = dummy_data;
|
|
|
|
tuningbuf = dmam_alloc_coherent(&pdev->dev, 128, &tuningbuf_pa, GFP_KERNEL);
|
|
|
|
/*Set timeout clock count and blk sz*/
|
|
blocksize = sdiohost_getblksize(host, host->id);
|
|
|
|
sdiohost_setblksize(host, host->id, 64);
|
|
|
|
timeout = (clk_get_rate(host->clk) / 1000) * 1000;
|
|
|
|
sdiohost_setdatatimeout(host, host->id, timeout);
|
|
|
|
tuning_destable[0].uisegaddr = tuningbuf_pa;
|
|
tuning_destable[0].uisegsize = 64;
|
|
|
|
sdiohost_setdestab(host->id, (uint32_t)tuning_destable, 1,
|
|
(uint32_t *)host->vuisdio_destab);
|
|
|
|
sdiohost_setupdatatransferdma(host, host->id, (u32)tuningbuf, 64,
|
|
SDIO_HOST_READ_DATA, host->vuisdio_destab);
|
|
|
|
if (sdiohost_sendsdcmd(host, host->id, opcode | SDIO_CMD_REG_NEED_RSP, 0)) {
|
|
|
|
sdiohost_resetdata(host, host->id);
|
|
pr_err("SDIO%d RCard: read tuning pattern error!!\r\n", host->id);
|
|
goto read_error;
|
|
}
|
|
|
|
if (wait_for_completion_timeout(&host->tuning_data_end, HZ*2) == 0)
|
|
goto read_error;
|
|
|
|
// Compare the data with expected tuning pattern
|
|
for (i = 0; i < 64; i++) {
|
|
if (tuningbuf[i] != golden_buf[i]) {
|
|
goto pattern_error;
|
|
}
|
|
}
|
|
|
|
status = host->status;
|
|
if ((status & SDIO_STATUS_REG_DATA_END) &&
|
|
(status & SDIO_STATUS_REG_DATA_CRC_OK))
|
|
pr_debug("Tuning pattern Data end or CRC ok\r\n");
|
|
|
|
sdiohost_setblksize(host, host->id, blocksize);
|
|
|
|
kfree(dummy_data);
|
|
|
|
dmam_free_coherent(&pdev->dev, 128, tuningbuf, tuningbuf_pa);
|
|
|
|
return E_OK;
|
|
|
|
pattern_error:
|
|
if (print_pattern_err) {
|
|
pr_err("Tuning pattern shoule be:\r\n");
|
|
for (i = 0; i < 64; i++) {
|
|
if (i != 0 && (i % 16 == 0)) {
|
|
pr_err("\n");
|
|
}
|
|
pr_err("%02x ", golden_buf[i]);
|
|
}
|
|
pr_err("nBut, pattern from card is:\n");
|
|
for (i = 0; i < 64; i++) {
|
|
if (i != 0 && (i % 16 == 0)) {
|
|
pr_err("\n");
|
|
}
|
|
pr_err("%02x ", tuningbuf[i]);
|
|
}
|
|
pr_err("\r\n");
|
|
}
|
|
|
|
read_error:
|
|
pr_err("SDIO%d Tuning error\r\n", host->id);
|
|
|
|
sdiohost_setblksize(host, host->id, blocksize);
|
|
|
|
kfree(dummy_data);
|
|
|
|
dmam_free_coherent(&pdev->dev, 128, tuningbuf, tuningbuf_pa);
|
|
|
|
return E_SYS;
|
|
}
|
|
|
|
void sdiohost_setphyclrdetval(struct mmc_nvt_host *host)
|
|
{
|
|
}
|
|
|
|
void sdiohost_setphydetch(struct mmc_nvt_host *host, u32 ch)
|
|
{
|
|
}
|
|
|
|
void sdiohost_setdlyphase_sel(struct mmc_nvt_host *host, u32 sel)
|
|
{
|
|
}
|
|
|
|
int sdiohost_getcmd(struct mmc_nvt_host *host)
|
|
{
|
|
return sdiohost_getreg(host, host->id, \
|
|
SDIO_CMD_REG_OFS) & SDIO_CMD_REG_INDEX;
|
|
}
|
|
|
|
void sdio_copy_info(struct mmc_nvt_host *info)
|
|
{
|
|
if (info->id == SDIO_HOST_ID_1)
|
|
drvdump_info[0] = *info;
|
|
else if (info->id == SDIO_HOST_ID_2)
|
|
drvdump_info[1] = *info;
|
|
else
|
|
drvdump_info[2] = *info;
|
|
}
|
|
|
|
void sdio_drvdump(void)
|
|
{
|
|
PAD_DRIVINGSINK driving;
|
|
PAD_POWER_STRUCT pad_power[1] = {0};
|
|
|
|
pr_info("SDIO1 Frequency %d Hz\n", (u32) clk_get_rate(drvdump_info[0].clk));
|
|
if (drvdump_info[0].cd_gpio)
|
|
pr_info("SDIO1 cd_gpio %d cd_detect_edge %d\n", drvdump_info[0].cd_gpio, drvdump_info[0].cd_detect_edge);
|
|
|
|
if (drvdump_info[0].cp_gpio)
|
|
pr_info("SDIO1 cp_gpio %d cp_gpio_value %d\n", drvdump_info[0].cp_gpio, drvdump_info[0].cp_gpio_value);
|
|
|
|
if (drvdump_info[0].max_voltage == SDIO_HOST_SETPOWER_VOL_1P8)
|
|
pr_info("SDIO1 max_voltage 1800 mV\n");
|
|
else
|
|
pr_info("SDIO1 max_voltage 3300 mV\n");
|
|
|
|
pad_power->pad_power_id = PAD_POWERID_MC0;
|
|
pad_get_power(pad_power);
|
|
|
|
if (pad_power->pad_power)
|
|
pr_info("SDIO1 pad_power 1800 mV\n");
|
|
else
|
|
pr_info("SDIO1 pad_power 3300 mV\n");
|
|
|
|
pr_info("SDIO1 voltage_switch %d\n", drvdump_info[0].voltage_switch);
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO11, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO1 CLK driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO1 CLK driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO1 CLK driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO1 CLK driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO1 CLK driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO1 CLK driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO1 CLK driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO1 CLK driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO1 CLK driving none mA\n");
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO12, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO1 CMD driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO1 CMD driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO1 CMD driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO1 CMD driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO1 CMD driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO1 CMD driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO1 CMD driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO1 CMD driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO1 CMD driving none mA\n");
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO13, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO1 D0 driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO1 D0 driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO1 D0 driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO1 D0 driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO1 D0 driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO1 D0 driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO1 D0 driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO1 D0 driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO1 D0 driving none mA\n");
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO14, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO1 D1 driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO1 D1 driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO1 D1 driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO1 D1 driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO1 D1 driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO1 D1 driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO1 D1 driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO1 D1 driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO1 D1 driving none mA\n");
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO15, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO1 D2 driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO1 D2 driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO1 D2 driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO1 D2 driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO1 D2 driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO1 D2 driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO1 D2 driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO1 D2 driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO1 D2 driving none mA\n");
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO16, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO1 D3 driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO1 D3 driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO1 D3 driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO1 D3 driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO1 D3 driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO1 D3 driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO1 D3 driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO1 D3 driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO1 D3 driving none mA\n");
|
|
|
|
pr_info("SDIO2 Frequency %d Hz\n", (u32) clk_get_rate(drvdump_info[1].clk));
|
|
if (drvdump_info[1].cd_gpio)
|
|
pr_info("SDIO2 cd_gpio %d cd_detect_edge %d\n", drvdump_info[1].cd_gpio, drvdump_info[1].cd_detect_edge);
|
|
|
|
if (drvdump_info[1].cp_gpio)
|
|
pr_info("SDIO2 cp_gpio %d cp_gpio_value %d\n", drvdump_info[1].cp_gpio, drvdump_info[1].cp_gpio_value);
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO17, &driving);
|
|
if (driving & PAD_DRIVINGSINK_5MA)
|
|
pr_info("SDIO2 CLK driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_10MA)
|
|
pr_info("SDIO2 CLK driving 10 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_15MA)
|
|
pr_info("SDIO2 CLK driving 15 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_20MA)
|
|
pr_info("SDIO2 CLK driving 20 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_25MA)
|
|
pr_info("SDIO2 CLK driving 25 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_30MA)
|
|
pr_info("SDIO2 CLK driving 30 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_35MA)
|
|
pr_info("SDIO2 CLK driving 35 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_40MA)
|
|
pr_info("SDIO2 CLK driving 40 mA\n");
|
|
else
|
|
pr_info("SDIO2 CLK driving none mA\n");
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO18, &driving);
|
|
if (nvt_get_chip_id() == CHIP_NA51055) {
|
|
if (driving & PAD_DRIVINGSINK_6MA)
|
|
pr_info("SDIO2 CMD driving 5 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 CMD driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 CMD driving none mA\n");
|
|
} else {
|
|
if (driving & PAD_DRIVINGSINK_4MA)
|
|
pr_info("SDIO2 CMD driving 4 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_8MA)
|
|
pr_info("SDIO2 CMD driving 8 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_12MA)
|
|
pr_info("SDIO2 CMD driving 12 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 CMD driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 CMD driving none mA\n");
|
|
}
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO19, &driving);
|
|
if (nvt_get_chip_id() == CHIP_NA51055) {
|
|
if (driving & PAD_DRIVINGSINK_6MA)
|
|
pr_info("SDIO2 D0 driving 6 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D0 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D0 driving none mA\n");
|
|
} else {
|
|
if (driving & PAD_DRIVINGSINK_4MA)
|
|
pr_info("SDIO2 D0 driving 4 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_8MA)
|
|
pr_info("SDIO2 D0 driving 8 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_12MA)
|
|
pr_info("SDIO2 D0 driving 12 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D0 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D0 driving none mA\n");
|
|
}
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO20, &driving);
|
|
if (nvt_get_chip_id() == CHIP_NA51055) {
|
|
if (driving & PAD_DRIVINGSINK_6MA)
|
|
pr_info("SDIO2 D1 driving 6 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D1 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D1 driving none mA\n");
|
|
} else {
|
|
if (driving & PAD_DRIVINGSINK_4MA)
|
|
pr_info("SDIO2 D1 driving 4 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_8MA)
|
|
pr_info("SDIO2 D1 driving 8 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_12MA)
|
|
pr_info("SDIO2 D1 driving 12 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D1 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D1 driving none mA\n");
|
|
}
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO21, &driving);
|
|
if (nvt_get_chip_id() == CHIP_NA51055) {
|
|
if (driving & PAD_DRIVINGSINK_6MA)
|
|
pr_info("SDIO2 D2 driving 6 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D2 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D2 driving none mA\n");
|
|
} else {
|
|
if (driving & PAD_DRIVINGSINK_4MA)
|
|
pr_info("SDIO2 D2 driving 4 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_8MA)
|
|
pr_info("SDIO2 D2 driving 8 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_12MA)
|
|
pr_info("SDIO2 D2 driving 12 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D2 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D2 driving none mA\n");
|
|
}
|
|
|
|
pad_get_drivingsink(PAD_DS_CGPIO22, &driving);
|
|
if (nvt_get_chip_id() == CHIP_NA51055) {
|
|
if (driving & PAD_DRIVINGSINK_6MA)
|
|
pr_info("SDIO2 D3 driving 6 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D3 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D3 driving none mA\n");
|
|
} else {
|
|
if (driving & PAD_DRIVINGSINK_4MA)
|
|
pr_info("SDIO2 D3 driving 4 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_8MA)
|
|
pr_info("SDIO2 D3 driving 8 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_12MA)
|
|
pr_info("SDIO2 D3 driving 12 mA\n");
|
|
else if (driving & PAD_DRIVINGSINK_16MA)
|
|
pr_info("SDIO2 D3 driving 16 mA\n");
|
|
else
|
|
pr_info("SDIO2 D3 driving none mA\n");
|
|
}
|
|
|
|
pr_info("SDIO3 Frequency %d Hz\n", (u32) clk_get_rate(drvdump_info[2].clk));
|
|
if (drvdump_info[2].cd_gpio)
|
|
pr_info("SDIO3 cd_gpio %d cd_detect_edge %d\n", drvdump_info[2].cd_gpio, drvdump_info[2].cd_detect_edge);
|
|
|
|
if (drvdump_info[2].cp_gpio)
|
|
pr_info("SDIO3 cp_gpio %d cp_gpio_value %d\n", drvdump_info[2].cp_gpio, drvdump_info[2].cp_gpio_value);
|
|
}
|
|
EXPORT_SYMBOL(sdio_drvdump);
|