#define xbwprintk(n, arg...)
#endif
-#if defined(CONFIG_ARCH_RK29)
-#define SDMMC_USE_INT_UNBUSY 0
-#else
-#define SDMMC_USE_INT_UNBUSY 0///1
-#endif
-
-/*
-** You can set the macro to true, if some module wants to use this feature, which is about SDIO suspend-resume.
-** As the following example.
-** added by xbw at 2013-05-08
-*/
-#if defined(CONFIG_MTK_COMBO_DRIVER_VERSION_JB2)
-#define RK_SDMMC_USE_SDIO_SUSPEND_RESUME 1
-#else
-#define RK_SDMMC_USE_SDIO_SUSPEND_RESUME 0
-#endif
-
-#define RK29_SDMMC_ERROR_FLAGS (SDMMC_INT_FRUN | SDMMC_INT_HLE )
-
-#if defined(CONFIG_SDMMC0_RK29_SDCARD_DET_FROM_GPIO)
- #if SDMMC_USE_INT_UNBUSY
- #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS )
- #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_TXDR | SDMMC_INT_RXDR )
- #else
- #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS )
- #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_TXDR | SDMMC_INT_RXDR )
- #endif
-#else
- #if SDMMC_USE_INT_UNBUSY
- #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD)
- #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD| SDMMC_INT_TXDR | SDMMC_INT_RXDR )
- #else
- #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD)
- #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD| SDMMC_INT_TXDR | SDMMC_INT_RXDR )
- #endif
-#endif
-
-#define RK29_SDMMC_SEND_START_TIMEOUT 3000 //The time interval from the time SEND_CMD to START_CMD_BIT cleared.
-#define RK29_ERROR_PRINTK_INTERVAL 200 //The time interval between the two printk for the same error.
-#define RK29_SDMMC_WAIT_DTO_INTERNVAL 4500 //The time interval from the CMD_DONE_INT to DTO_INT
-#define RK29_SDMMC_REMOVAL_DELAY 2000 //The time interval from the CD_INT to detect_timer react.
-
-#define RK29_SDMMC_VERSION "Ver.6.00 The last modify date is 2013-08-02"
-
-#if !defined(CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD)
-#define RK29_CTRL_SDMMC_ID 0 //mainly used by SDMMC
-#define RK29_CTRL_SDIO1_ID 1 //mainly used by sdio-wifi
-#define RK29_CTRL_SDIO2_ID 2 //mainly used by sdio-card
-#else
-#define RK29_CTRL_SDMMC_ID 5
-#define RK29_CTRL_SDIO1_ID 1
-#define RK29_CTRL_SDIO2_ID 2
-#endif
-
-#define SDMMC_CLOCK_TEST 0
-
-#define RK29_SDMMC_NOTIFY_REMOVE_INSERTION /* use sysfs to notify the removal or insertion of sd-card*/
-//#define RK29_SDMMC_LIST_QUEUE /* use list-queue for multi-card*/
+#define RK29_SDMMC_VERSION "Ver.6.01 The last modify date is 2013-08-05"
#define RK29_SDMMC_DEFAULT_SDIO_FREQ 0 // 1--run in default frequency(50Mhz); 0---run in 25Mhz,
#if defined(CONFIG_MT6620)|| defined(CONFIG_ESP8089)
#define DRIVER_SDMMC_USE_NEW_IOMUX_API 0
#endif
-//support Internal DMA
-#if 0 //Sometime in the future to enable
-#define DRIVER_SDMMC_USE_IDMA 1
-#else
-#define DRIVER_SDMMC_USE_IDMA 0
-#endif
-
#define SWITCH_VOLTAGE_18_33 0 //RK30_PIN2_PD7 //Temporary experiment
#define SWITCH_VOLTAGE_ENABLE_VALUE_33 GPIO_LOW
-enum {
- EVENT_CMD_COMPLETE = 0,
- EVENT_DATA_COMPLETE,
- EVENT_DATA_UNBUSY,
- EVENT_DATA_ERROR,
- EVENT_XFER_ERROR
-};
-
-enum rk29_sdmmc_state {
- STATE_IDLE = 0,
- STATE_SENDING_CMD,
- STATE_DATA_BUSY,
- STATE_DATA_UNBUSY,
- STATE_DATA_END,
- STATE_SENDING_STOP,
-};
-
-struct rk29_sdmmc_dma_info {
- enum dma_ch chn;
- char *name;
- struct rk29_dma_client client;
-};
-
-static struct rk29_sdmmc_dma_info rk29_sdmmc_dma_infos[]= {
- {
- .chn = DMACH_SDMMC,
- .client = {
- .name = "rk29-dma-sdmmc0",
- }
- },
- {
- .chn = DMACH_SDIO,
- .client = {
- .name = "rk29-dma-sdio1",
- }
- },
-
- {
- .chn = DMACH_EMMC,
- .client = {
- .name = "rk29-dma-sdio2",
- }
- },
-};
-
-
-/* Interrupt Information */
-typedef struct TagSDC_INT_INFO
-{
- u32 transLen; //the length of data sent.
- u32 desLen; //the total length of the all data.
- u32 *pBuf; //the data buffer for interrupt read or write.
-}SDC_INT_INFO_T;
-
-
-struct rk29_sdmmc {
- spinlock_t lock;
- void __iomem *regs;
- struct clk *clk;
-
- struct mmc_request *mrq;
- struct mmc_request *new_mrq;
- struct mmc_command *cmd;
- struct mmc_data *data;
- struct scatterlist *sg;
-
- dma_addr_t dma_addr;;
- unsigned int use_dma:1;
- char dma_name[8];
- u32 cmd_status;
- u32 data_status;
- u32 stop_cmdr;
-
- u32 old_div;
- u32 cmdr; //the value setted into command-register
- u32 dodma; //sign the DMA used for transfer.
- u32 errorstep;//record the error point.
- int timeout_times; //use to force close the sdmmc0 when the timeout_times exceeds the limit.
- u32 *pbuf;
- SDC_INT_INFO_T intInfo;
- struct rk29_sdmmc_dma_info dma_info;
- int irq;
- int error_times;
- u32 old_cmd;
-
- struct tasklet_struct tasklet;
- unsigned long pending_events;
- unsigned long completed_events;
- enum rk29_sdmmc_state state;
-
-#ifdef RK29_SDMMC_LIST_QUEUE
- struct list_head queue;
- struct list_head queue_node;
-#endif
-
- u32 bus_hz;
- struct platform_device *pdev;
- struct mmc_host *mmc;
- u32 ctype;
- unsigned int clock;
- unsigned long flags;
-
-#define RK29_SDMMC_CARD_PRESENT 0
-
- int id;
-
- struct timer_list detect_timer;
- struct timer_list request_timer; //the timer for INT_CMD_DONE
- struct timer_list DTO_timer; //the timer for INT_DTO
- struct mmc_command stopcmd;
- struct rksdmmc_gpio det_pin;
-
- /* flag for current bus settings */
- u32 bus_mode;
-
- unsigned int oldstatus;
- unsigned int complete_done;
- unsigned int retryfunc;
-
- int gpio_irq;
- int gpio_power_en;
- int gpio_power_en_level;
- struct delayed_work work;
-
-#ifdef CONFIG_RK29_SDIO_IRQ_FROM_GPIO
- unsigned int sdio_INT_gpio;
- unsigned int sdio_irq;
- unsigned long trigger_level;
-#endif
-
-#if defined(CONFIG_SDMMC0_RK29_WRITE_PROTECT) || defined(CONFIG_SDMMC1_RK29_WRITE_PROTECT)
- int write_protect;
- int protect_level;
-#endif
-
- bool irq_state;
- void (*set_iomux)(int device_id, unsigned int bus_width);
-
-};
-
-
-#ifdef RK29_SDMMC_NOTIFY_REMOVE_INSERTION
-static struct rk29_sdmmc *globalSDhost[3];
-#endif
-
-#define rk29_sdmmc_test_and_clear_pending(host, event) \
- test_and_clear_bit(event, &host->pending_events)
-#define rk29_sdmmc_test_pending(host, event) \
- test_bit(event, &host->pending_events)
-#define rk29_sdmmc_test_completed(host, event) \
- test_bit(event, &host->completed_events)
-#define rk29_sdmmc_set_completed(host, event) \
- set_bit(event, &host->completed_events)
-#define rk29_sdmmc_set_pending(host, event) \
- set_bit(event, &host->pending_events)
-
static void rk29_sdmmc_start_error(struct rk29_sdmmc *host);
static int rk29_sdmmc_clear_fifo(struct rk29_sdmmc *host);
int rk29_sdmmc_hw_init(void *data);
static int rk29_sdmmc_regs_printk(struct rk29_sdmmc *host)
{
- printk("SDMMC_CTRL: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CTRL));
- printk("SDMMC_PWREN: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_PWREN));
- printk("SDMMC_CLKDIV: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CLKDIV));
- printk("SDMMC_CLKSRC: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CLKSRC));
- printk("SDMMC_CLKENA: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CLKENA));
- printk("SDMMC_TMOUT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_TMOUT));
- printk("SDMMC_CTYPE: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CTYPE));
- printk("SDMMC_BLKSIZ: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_BLKSIZ));
- printk("SDMMC_BYTCNT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_BYTCNT));
- printk("SDMMC_INTMASK:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_INTMASK));
- printk("SDMMC_CMDARG: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CMDARG));
- printk("SDMMC_CMD: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CMD));
- printk("SDMMC_RESP0: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP0));
- printk("SDMMC_RESP1: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP1));
- printk("SDMMC_RESP2: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP2));
- printk("SDMMC_RESP3: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP3));
- printk("SDMMC_MINTSTS:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_MINTSTS));
- printk("SDMMC_RINTSTS:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RINTSTS));
- printk("SDMMC_STATUS: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_STATUS));
- printk("SDMMC_FIFOTH: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_FIFOTH));
- printk("SDMMC_CDETECT:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CDETECT));
- printk("SDMMC_WRTPRT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_WRTPRT));
- printk("SDMMC_TCBCNT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_TCBCNT));
- printk("SDMMC_TBBCNT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_TBBCNT));
- printk("SDMMC_DEBNCE: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_DEBNCE));
- printk("SDMMC_USRID: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_USRID));
-
-#if !defined(CONFIG_ARCH_RK29)
- printk("SDMMC_VERID: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_VERID));
- printk("SDMMC_UHS_REG:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_UHS_REG));
- printk("SDMMC_RST_n: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RST_n));
- printk("SDMMC_CARDTHRCTL: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CARDTHRCTL));
- printk("SDMMC_BACK_END_POWER: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_BACK_END_POWER));
-#endif
+ struct sdmmc_reg *regs = rk_sdmmc_regs;
+
+ while( regs->name != 0 )
+ {
+ printk("%s: (0x%04x) = 0x%08x\n", regs->name, regs->addr, rk29_sdmmc_read(host->regs, regs->addr));
+ regs++;
+ }
+
printk("=======printk %s-register end =========\n", host->dma_name);
return 0;
}
unsigned long flags;
if(!host)
- {
return;
- }
local_irq_save(flags);
if(host->irq_state != irqflag)
#ifdef RK29_SDMMC_NOTIFY_REMOVE_INSERTION
+/*
+** debug the progress.
+**
+** # echo version > sys/sd-sdio/rescan //check the current sdmmc-driver version
+**
+** # echo sd-reset > sys/sd-sdio/rescan //run mmc0 mmc_rescan again
+** # echo sd-regs > sys/sd-sdio/rescan //printk all registers of mmc0.
+**
+** # echo sdio1-reset > sys/sd-sdio/rescan //run mmc1 mmc_rescan again
+** # echo sdio1-regs > sys/sd-sdio/rescan //printk all registers of mmc1.
+**
+*/
ssize_t rk29_sdmmc_progress_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
return count;
}
-
-
struct kobj_attribute mmc_reset_attrs =
{
.attr = {
struct kobject *parentkobject;
struct kobject * me = kmalloc(sizeof(struct kobject) , GFP_KERNEL );
if(!me)
- {
return -ENOMEM;
- }
+
memset(me ,0,sizeof(struct kobject));
kobject_init( me , &mmc_kset_ktype );
}
#endif
-#if defined (CONFIG_DEBUG_FS)
-static int rk29_sdmmc_regs_show(struct seq_file *s, void *v)
-{
- struct rk29_sdmmc *host = s->private;
-
- seq_printf(s, "SDMMC_CTRL: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CTRL));
- seq_printf(s, "SDMMC_PWREN: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_PWREN));
- seq_printf(s, "SDMMC_CLKDIV: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CLKDIV));
- seq_printf(s, "SDMMC_CLKSRC: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CLKSRC));
- seq_printf(s, "SDMMC_CLKENA: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CLKENA));
- seq_printf(s, "SDMMC_TMOUT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_TMOUT));
- seq_printf(s, "SDMMC_CTYPE: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CTYPE));
- seq_printf(s, "SDMMC_BLKSIZ: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_BLKSIZ));
- seq_printf(s, "SDMMC_BYTCNT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_BYTCNT));
- seq_printf(s, "SDMMC_INTMASK:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_INTMASK));
- seq_printf(s, "SDMMC_CMDARG: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CMDARG));
- seq_printf(s, "SDMMC_CMD: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CMD));
- seq_printf(s, "SDMMC_RESP0: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP0));
- seq_printf(s, "SDMMC_RESP1: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP1));
- seq_printf(s, "SDMMC_RESP2: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP2));
- seq_printf(s, "SDMMC_RESP3: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RESP3));
- seq_printf(s, "SDMMC_MINTSTS:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_MINTSTS));
- seq_printf(s, "SDMMC_RINTSTS:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_RINTSTS));
- seq_printf(s, "SDMMC_STATUS: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_STATUS));
- seq_printf(s, "SDMMC_FIFOTH: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_FIFOTH));
- seq_printf(s, "SDMMC_CDETECT:\t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_CDETECT));
- seq_printf(s, "SDMMC_WRTPRT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_WRTPRT));
- seq_printf(s, "SDMMC_TCBCNT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_TCBCNT));
- seq_printf(s, "SDMMC_TBBCNT: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_TBBCNT));
- seq_printf(s, "SDMMC_DEBNCE: \t0x%08x\n", rk29_sdmmc_read(host->regs, SDMMC_DEBNCE));
-
- return 0;
-}
-
-
-/*
- * The debugfs stuff below is mostly optimized away when
- * CONFIG_DEBUG_FS is not set.
- */
-static int rk29_sdmmc_req_show(struct seq_file *s, void *v)
-{
- struct rk29_sdmmc *host = s->private;
- struct mmc_request *mrq;
- struct mmc_command *cmd;
- struct mmc_command *stop;
- struct mmc_data *data;
-
- /* Make sure we get a consistent snapshot */
- spin_lock(&host->lock);
-
- mrq = host->mrq;
-
- if (mrq) {
- cmd = mrq->cmd;
- data = mrq->data;
- stop = mrq->stop;
-
- if (cmd)
- seq_printf(s,
- "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
- cmd->opcode, cmd->arg, cmd->flags,
- cmd->resp[0], cmd->resp[1], cmd->resp[2],
- cmd->resp[2], cmd->error);
- if (data)
- seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
- data->bytes_xfered, data->blocks,
- data->blksz, data->flags, data->error);
- if (stop)
- seq_printf(s,
- "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
- stop->opcode, stop->arg, stop->flags,
- stop->resp[0], stop->resp[1], stop->resp[2],
- stop->resp[2], stop->error);
- }
-
- spin_unlock(&host->lock);
-
- return 0;
-}
-
-static int rk29_sdmmc_req_open(struct inode *inode, struct file *file)
-{
- return single_open(file, rk29_sdmmc_req_show, inode->i_private);
-}
-
-static const struct file_operations rk29_sdmmc_req_fops = {
- .owner = THIS_MODULE,
- .open = rk29_sdmmc_req_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-
-static int rk29_sdmmc_regs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, rk29_sdmmc_regs_show, inode->i_private);
-}
-
-static const struct file_operations rk29_sdmmc_regs_fops = {
- .owner = THIS_MODULE,
- .open = rk29_sdmmc_regs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void rk29_sdmmc_init_debugfs(struct rk29_sdmmc *host)
-{
- struct mmc_host *mmc = host->mmc;
- struct dentry *root;
- struct dentry *node;
-
- root = mmc->debugfs_root;
- if (!root)
- return;
-
- node = debugfs_create_file("regs", S_IRUSR, root, host,
- &rk29_sdmmc_regs_fops);
- if (IS_ERR(node))
- return;
- if (!node)
- goto err;
-
- node = debugfs_create_file("req", S_IRUSR, root, host, &rk29_sdmmc_req_fops);
- if (!node)
- goto err;
-
- node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
- if (!node)
- goto err;
-
- node = debugfs_create_x32("pending_events", S_IRUSR, root,
- (u32 *)&host->pending_events);
- if (!node)
- goto err;
-
- node = debugfs_create_x32("completed_events", S_IRUSR, root,
- (u32 *)&host->completed_events);
- if (!node)
- goto err;
-
- return;
-
-err:
- dev_err(&mmc->class_dev, "failed to initialize debugfs for host\n");
-}
-#endif
-
-
-
/**
** This function checks whether the core supports the IDMAC.
** return Returns 1 if HW supports IDMAC, else returns 0.
return retval;
}
-
-
-
static u32 rk29_sdmmc_prepare_command(struct mmc_command *cmd)
{
u32 cmdr = cmd->opcode;
case MMC_GO_IDLE_STATE:
cmdr |= (SDMMC_CMD_INIT | SDMMC_CMD_PRV_DAT_NO_WAIT);
break;
-
case MMC_STOP_TRANSMISSION:
cmdr |= (SDMMC_CMD_STOP | SDMMC_CMD_PRV_DAT_NO_WAIT);
break;
case MMC_GO_INACTIVE_STATE:
cmdr |= SDMMC_CMD_PRV_DAT_NO_WAIT;
break;
-
default:
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
break;
u32 value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
if (enable)
- {
value |= SDMMC_CTRL_DMA_ENABLE;
- }
else
- {
value &= ~(SDMMC_CTRL_DMA_ENABLE);
- }
rk29_sdmmc_write(host->regs, SDMMC_CTRL, value);
}
static void send_stop_cmd(struct rk29_sdmmc *host)
{
int ret, i;
- // int timeout = 250;
- // unsigned int value;
-
-
+
if(host->mrq->cmd->error)
{
//stop DMA
host->intInfo.transLen = host->intInfo.desLen;
}
+static void rk_sdmmc_push_data32(struct rk29_sdmmc *host, void *buf, int cnt)
+{
+ u32 *pdata = (u32 *)buf;
+
+ WARN_ON(cnt % 4 != 0);
+ WARN_ON((unsigned long)pdata & 0x3);
+
+ cnt = cnt >> 2;
+ while (cnt > 0) {
+ rk29_sdmmc_write(host->regs, SDMMC_DATA, *pdata++);
+ cnt--;
+ }
+}
+
+static void rk_sdmmc_pull_data32(struct rk29_sdmmc *host, void *buf, int cnt)
+{
+ u32 *pdata = (u32 *)buf;
+
+ WARN_ON(cnt % 4 != 0);
+ WARN_ON((unsigned long)pdata & 0x3);
+
+ cnt = cnt >> 2;
+ while (cnt > 0) {
+ *pdata++ = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+ cnt--;
+ }
+}
+
static int rk29_sdmmc_read_data_pio(struct rk29_sdmmc *host)
{
struct scatterlist *sg;
- u32 *buf;
- unsigned int offset = 0;
- struct mmc_data *data;
+ void *buf;
+ unsigned int offset;
+ struct mmc_data *data = host->data;
u32 value;
- unsigned int nbytes = 0;
- int remaining;
+ unsigned int nbytes=0, len;
value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
if ( value& SDMMC_STAUTS_FIFO_EMPTY)
if((NULL == host)&&(NULL == host->data))
goto done;
-
- data = host->data;
+
sg = host->sg;
- buf = (u32 *)sg_virt(sg);
+ buf = sg_virt(sg);
+ offset = host->pio_offset;
while ( (host->intInfo.transLen < host->intInfo.desLen) && (!(value & SDMMC_STAUTS_FIFO_EMPTY)) )
{
- if( ((offset + (host->intInfo.desLen - host->intInfo.transLen))<<2) <= sg->length )
- {
- buf[offset] = rk29_sdmmc_read(host->regs, SDMMC_DATA);
- offset ++;
- nbytes += 4;
- host->intInfo.transLen++;
+ len = SDMMC_GET_FCNT(value) << PIO_DATA_SHIFT;
+ if (offset + len <= sg->length) {
+ host->pull_data(host, (void *)(buf + offset), len);
- if ((offset<<2) == sg->length)
- {
+ offset += len;
+ nbytes += len;
+ host->intInfo.transLen++;
+
+ if (offset == sg->length) {
flush_dcache_page(sg_page(sg));
host->sg = sg = sg_next(sg);
if (!sg)
goto done;
offset = 0;
- buf = (u32 *)sg_virt(sg);
+ buf = sg_virt(sg);
}
-
- }
- else
- {
- remaining = (sg->length>>2) - offset;
- while( remaining>0)
- {
- buf[offset] = rk29_sdmmc_read(host->regs, SDMMC_DATA);
- offset ++;
- nbytes += 4;
- remaining --;
- host->intInfo.transLen++;
- }
-
- flush_dcache_page(sg_page(sg));
-
- host->sg = sg = sg_next(sg);
- if (!sg)
- goto done;
-
- offset = 0;
- buf = (u32 *)sg_virt(sg);
- }
-
+ }else {
+ unsigned int remaining = sg->length - offset;
+ host->pull_data(host, (void *)(buf + offset),remaining);
+ nbytes += remaining;
+ host->intInfo.transLen++;
+
+ flush_dcache_page(sg_page(sg));
+ host->sg = sg = sg_next(sg);
+ if (!sg)
+ goto done;
+
+ offset = len - remaining;
+ buf = sg_virt(sg);
+ host->pull_data(host, buf, offset);
+ nbytes += offset;
+ }
+
+ host->pio_offset = offset;
data->bytes_xfered += nbytes;
value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
}
-
+ return 0;
done:
+ data->bytes_xfered += nbytes;
return 0;
}
static int rk29_sdmmc_write_data_pio(struct rk29_sdmmc *host)
{
struct scatterlist *sg;
- u32 *buf;
- unsigned int offset = 0;
- struct mmc_data *data;
+ void *buf;
+ unsigned int offset;
+ struct mmc_data *data = host->data;
u32 value;
- unsigned int nbytes = 0;
- int remaining;
+ unsigned int nbytes=0, len;
value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
if ( value& SDMMC_STAUTS_FIFO_EMPTY)
if((NULL == host)&&(NULL == host->data))
goto done;
- data = host->data;
sg = host->sg;
- buf = (u32 *)sg_virt(sg);
-
+ buf = sg_virt(sg);
+ offset = host->pio_offset;
+
while ( (host->intInfo.transLen < host->intInfo.desLen) && (!(value & SDMMC_STAUTS_FIFO_EMPTY)) )
{
- if( ((offset + (host->intInfo.desLen - host->intInfo.transLen))<<2) <= sg->length )
- {
- rk29_sdmmc_write(host->regs, SDMMC_DATA, buf[offset]);
- offset ++;
- nbytes += 4;
- host->intInfo.transLen++;
+ len = SDMMC_FIFO_SZ -(SDMMC_GET_FCNT(value) << PIO_DATA_SHIFT);
+ if (offset + len <= sg->length) {
+ host->push_data(host, (void *)(buf + offset), len);
- if ((offset<<2) == sg->length)
- {
+ offset += len;
+ nbytes += len;
+ host->intInfo.transLen++;
+ if (offset == sg->length) {
host->sg = sg = sg_next(sg);
if (!sg)
goto done;
offset = 0;
- buf = (u32 *)sg_virt(sg);
+ buf = sg_virt(sg);
}
-
- }
- else
- {
- remaining = (sg->length>>2) - offset;
- while( remaining>0)
- {
- rk29_sdmmc_write(host->regs, SDMMC_DATA, buf[offset]);
- offset ++;
- nbytes += 4;
- remaining --;
- host->intInfo.transLen++;
- }
-
- host->sg = sg = sg_next(sg);
- if (!sg)
- goto done;
-
- offset = 0;
- buf = (u32 *)sg_virt(sg);
- }
+ } else {
+ unsigned int remaining = sg->length - offset;
+
+ host->push_data(host, (void *)(buf + offset),
+ remaining);
+ nbytes += remaining;
+ host->intInfo.transLen++;
+
+ host->sg = sg = sg_next(sg);
+ if (!sg)
+ goto done;
+
+ offset = len - remaining;
+ buf = sg_virt(sg);
+ host->push_data(host, (void *)buf, offset);
+ nbytes += offset;
+ }
- data->bytes_xfered += nbytes;
+ host->pio_offset = offset;
+ data->bytes_xfered += nbytes;
value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
}
+ return 0;
+
done:
+ data->bytes_xfered += nbytes;
return 0;
}
static int rk29_sdmmc_submit_data_dma(struct rk29_sdmmc *host, struct mmc_data *data)
{
- unsigned int i,direction, sgDirection;
+ unsigned int i,direction, sgDirection;
int ret, dma_len=0;
if(host->use_dma == 0)
host->intInfo.transLen = 0;
host->intInfo.pBuf = (u32 *)pBuf;
- if(0)//(host->intInfo.desLen <= 512 )
+ if(0)//(host->intInfo.desLen <= TX_WMARK)
{
- //use pio-mode
+ //use pio-mode
+ rk29_sdmmc_write_data_pio(host);
return SDM_SUCCESS;
}
else
return output;
}
-
-
-
static int rk29_sdmmc_prepare_read_data(struct rk29_sdmmc *host, struct mmc_data *data)
{
u32 count = 0;
if(count > (RX_WMARK+1)) //datasheet error.actually, it can nont waken the interrupt when less and equal than RX_WMARK+1
{
- if(0) //(host->intInfo.desLen <= 512 )
+ if(0)//(host->intInfo.desLen <= RX_WMARK)
{
//use pio-mode
+ rk29_sdmmc_read_data_pio(host);
return SDM_SUCCESS;
}
else
data->bytes_xfered = 0;
host->pbuf = (u32*)sg_virt(data->sg);
+ host->pio_offset = 0;
if (data->flags & MMC_DATA_STREAM)
{
host->dma_addr = regs->start + SDMMC_DATA;
}
+ /*
+ * Get the host data width,default 32bit
+ */
+ host->push_data = rk_sdmmc_push_data32;
+ host->pull_data = rk_sdmmc_pull_data32;
+
#if defined(CONFIG_SDMMC0_RK29_WRITE_PROTECT) || defined(CONFIG_SDMMC1_RK29_WRITE_PROTECT)
host->write_protect = pdata->write_prt;
host->protect_level = pdata->write_prt_enalbe_level;
}
#endif
-#if defined (CONFIG_DEBUG_FS)
- rk29_sdmmc_init_debugfs(host);
-#endif
-
printk(KERN_INFO ".Line%d..The End of SDMMC-probe %s. [%s]\n", __LINE__, RK29_SDMMC_VERSION,host->dma_name);
return 0;
#define SDMMC_DATA SDMMC_FIFO_BASE
#endif
+struct sdmmc_reg
+{
+ u32 addr;
+ char * name;
+};
+
+static struct sdmmc_reg rk_sdmmc_regs[] =
+{
+ { 0x0000, " CTRL" },
+ { 0x0004, " PWREN" },
+ { 0x0008, " CLKDIV" },
+ { 0x000C, " CLKSRC" },
+ { 0x0010, " CLKENA" },
+ { 0x0014, " TMOUT" },
+ { 0x0018, " CTYPE" },
+ { 0x001C, " BLKSIZ" },
+ { 0x0020, " BYTCNT" },
+ { 0x0024, " INTMSK" },
+ { 0x0028, " CMDARG" },
+ { 0x002C, " CMD" },
+ { 0x0030, " RESP0" },
+ { 0x0034, " RESP1" },
+ { 0x0038, " RESP2" },
+ { 0x003C, " RESP3" },
+ { 0x0040, " MINSTS" },
+ { 0x0044, " RINTSTS" },
+ { 0x0048, " STATUS" },
+ { 0x004C, " FIFOTH" },
+ { 0x0050, " CDETECT" },
+ { 0x0054, " WRTPRT" },
+ { 0x0058, " GPIO" },
+ { 0x005C, " TCBCNT" },
+ { 0x0060, " TBBCNT" },
+ { 0x0064, " DEBNCE" },
+ { 0x0068, " USRID" },
+#if !defined(CONFIG_ARCH_RK29)
+ { 0x006C, " VERID" },
+ { 0x0070, " HCON" },
+ { 0x0074, " UHS_REG" },
+ { 0x0078, " RST_n" },
+ { 0x0080, " BMOD" },
+ { 0x0084, " PLDMND" },
+ { 0x0088, " DBADDR" },
+ { 0x008C, " IDSTS" },
+ { 0x0090, " IDINTEN" },
+ { 0x0094, " DSCADDR" },
+ { 0x0098, " BUFADDR" },
+ { 0x0100, "CARDTHRCTL" },
+ { 0x0104, "BackEndPwr" },
+#endif
+ { 0, 0 }
+};
+
#define BIT(n) (1<<(n))
#define RK_CLEAR_BIT(n) (0<<(n))
#define SDMMC_CLKEN_LOW_PWR BIT(16)
#define SDMMC_CLKEN_NO_LOW_PWR RK_CLEAR_BIT(16) //low-power mode disabled
#define SDMMC_CLKEN_ENABLE BIT(0)
-#define SDMMC_CLKEN_DISABLE RK_CLEAR_BIT(16) //clock disabled
+#define SDMMC_CLKEN_DISABLE RK_CLEAR_BIT(0) //clock disabled
/* time-out register defines(base+0x14) */
#define SDMMC_TMOUT_DATA(n) _SBF(8, (n))
#define SDMMC_STAUTS_FIFO_EMPTY BIT(2) //FIFO is empty status
/* Status register defines */
-#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FF)//fifo_count, numbers of filled locations in FIFO
+#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)//fifo_count, numbers of filled locations in FIFO
#define SDMMC_FIFO_SZ 32
+#define PIO_DATA_SHIFT 2
/* FIFO Register (base + 0x4c)*/
#define SDM_WAIT_FOR_CMDSTART_TIMEOUT (12)
#define SDM_WAIT_FOR_FIFORESET_TIMEOUT (13)
-
-
#define FALSE 0
#define TRUE 1
-
#define DEBOUNCE_TIME (25) //uint is ms, recommend 5--25ms
+#if defined(CONFIG_ARCH_RK29)
+#define SDMMC_USE_INT_UNBUSY 0
+#else
+#define SDMMC_USE_INT_UNBUSY 0///1
+#endif
+
+/*
+** You can set the macro to true, if some module wants to use this feature, which is about SDIO suspend-resume.
+** As the following example.
+** added by xbw at 2013-05-08
+*/
+#if defined(CONFIG_MTK_COMBO_DRIVER_VERSION_JB2)
+#define RK_SDMMC_USE_SDIO_SUSPEND_RESUME 1
+#else
+#define RK_SDMMC_USE_SDIO_SUSPEND_RESUME 0
+#endif
+
+#define RK29_SDMMC_ERROR_FLAGS (SDMMC_INT_FRUN | SDMMC_INT_HLE )
+
+#if defined(CONFIG_SDMMC0_RK29_SDCARD_DET_FROM_GPIO)
+ #if SDMMC_USE_INT_UNBUSY
+ #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS )
+ #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_TXDR | SDMMC_INT_RXDR )
+ #else
+ #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS )
+ #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_TXDR | SDMMC_INT_RXDR )
+ #endif
+#else
+ #if SDMMC_USE_INT_UNBUSY
+ #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD)
+ #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_UNBUSY |RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD| SDMMC_INT_TXDR | SDMMC_INT_RXDR )
+ #else
+ #define RK29_SDMMC_INTMASK_USEDMA (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD)
+ #define RK29_SDMMC_INTMASK_USEIO (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD| SDMMC_INT_TXDR | SDMMC_INT_RXDR )
+ #endif
+#endif
+
+#define RK29_SDMMC_SEND_START_TIMEOUT 3000 //The time interval from the time SEND_CMD to START_CMD_BIT cleared.
+#define RK29_ERROR_PRINTK_INTERVAL 200 //The time interval between the two printk for the same error.
+#define RK29_SDMMC_WAIT_DTO_INTERNVAL 4500 //The time interval from the CMD_DONE_INT to DTO_INT
+#define RK29_SDMMC_REMOVAL_DELAY 2000 //The time interval from the CD_INT to detect_timer react.
+
+//#define RK29_SDMMC_VERSION "Ver.6.00 The last modify date is 2013-08-02"
+
+#if !defined(CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD)
+#define RK29_CTRL_SDMMC_ID 0 //mainly used by SDMMC
+#define RK29_CTRL_SDIO1_ID 1 //mainly used by sdio-wifi
+#define RK29_CTRL_SDIO2_ID 2 //mainly used by sdio-card
+#else
+#define RK29_CTRL_SDMMC_ID 5
+#define RK29_CTRL_SDIO1_ID 1
+#define RK29_CTRL_SDIO2_ID 2
+#endif
+
+#define SDMMC_CLOCK_TEST 0
+
+#define RK29_SDMMC_NOTIFY_REMOVE_INSERTION /* use sysfs to notify the removal or insertion of sd-card*/
+//#define RK29_SDMMC_LIST_QUEUE /* use list-queue for multi-card*/
+
+//support Internal DMA
+#if 0 //Sometime in the future to enable
+#define DRIVER_SDMMC_USE_IDMA 1
+#else
+#define DRIVER_SDMMC_USE_IDMA 0
+#endif
+
+
+enum {
+ EVENT_CMD_COMPLETE = 0,
+ EVENT_DATA_COMPLETE,
+ EVENT_DATA_UNBUSY,
+ EVENT_DATA_ERROR,
+ EVENT_XFER_ERROR
+};
+
+enum rk29_sdmmc_state {
+ STATE_IDLE = 0,
+ STATE_SENDING_CMD,
+ STATE_DATA_BUSY,
+ STATE_DATA_UNBUSY,
+ STATE_DATA_END,
+ STATE_SENDING_STOP,
+};
+
+struct rk29_sdmmc_dma_info {
+ enum dma_ch chn;
+ char *name;
+ struct rk29_dma_client client;
+};
+
+static struct rk29_sdmmc_dma_info rk29_sdmmc_dma_infos[]= {
+ {
+ .chn = DMACH_SDMMC,
+ .client = {
+ .name = "rk29-dma-sdmmc0",
+ }
+ },
+ {
+ .chn = DMACH_SDIO,
+ .client = {
+ .name = "rk29-dma-sdio1",
+ }
+ },
+
+ {
+ .chn = DMACH_EMMC,
+ .client = {
+ .name = "rk29-dma-sdio2",
+ }
+ },
+};
+
+
+/* Interrupt Information */
+typedef struct TagSDC_INT_INFO
+{
+ u32 transLen; //the length of data sent.
+ u32 desLen; //the total length of the all data.
+ u32 *pBuf; //the data buffer for interrupt read or write.
+}SDC_INT_INFO_T;
+
+
+struct rk29_sdmmc {
+ spinlock_t lock;
+ void __iomem *regs;
+ struct clk *clk;
+
+ struct mmc_request *mrq;
+ struct mmc_request *new_mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct scatterlist *sg;
+ unsigned int pio_offset;
+
+ dma_addr_t dma_addr;;
+ unsigned int use_dma:1;
+ char dma_name[8];
+ u32 cmd_status;
+ u32 data_status;
+ u32 stop_cmdr;
+
+ u32 old_div;
+ u32 cmdr; //the value setted into command-register
+ u32 dodma; //sign the DMA used for transfer.
+ u32 errorstep;//record the error point.
+ int timeout_times; //use to force close the sdmmc0 when the timeout_times exceeds the limit.
+ u32 *pbuf;
+ SDC_INT_INFO_T intInfo;
+ struct rk29_sdmmc_dma_info dma_info;
+ int irq;
+ int error_times;
+ u32 old_cmd;
+
+ struct tasklet_struct tasklet;
+ unsigned long pending_events;
+ unsigned long completed_events;
+ enum rk29_sdmmc_state state;
+
+#ifdef RK29_SDMMC_LIST_QUEUE
+ struct list_head queue;
+ struct list_head queue_node;
+#endif
+
+ u32 bus_hz;
+ struct platform_device *pdev;
+ struct mmc_host *mmc;
+ u32 ctype;
+ unsigned int clock;
+ unsigned long flags;
+
+#define RK29_SDMMC_CARD_PRESENT 0
+
+ int id;
+
+ struct timer_list detect_timer;
+ struct timer_list request_timer; //the timer for INT_CMD_DONE
+ struct timer_list DTO_timer; //the timer for INT_DTO
+ struct mmc_command stopcmd;
+ struct rksdmmc_gpio det_pin;
+
+ /* flag for current bus settings */
+ u32 bus_mode;
+
+ unsigned int oldstatus;
+ unsigned int complete_done;
+ unsigned int retryfunc;
+
+ int gpio_irq;
+ int gpio_power_en;
+ int gpio_power_en_level;
+ struct delayed_work work;
+
+#ifdef CONFIG_RK29_SDIO_IRQ_FROM_GPIO
+ unsigned int sdio_INT_gpio;
+ unsigned int sdio_irq;
+ unsigned long trigger_level;
+#endif
+
+#if defined(CONFIG_SDMMC0_RK29_WRITE_PROTECT) || defined(CONFIG_SDMMC1_RK29_WRITE_PROTECT)
+ int write_protect;
+ int protect_level;
+#endif
+
+ bool irq_state;
+ void (*set_iomux)(int device_id, unsigned int bus_width);
+
+ /* FIFO push and pull */
+ int data_shift;
+ void (*push_data)(struct rk29_sdmmc *host, void *buf, int cnt);
+ void (*pull_data)(struct rk29_sdmmc *host, void *buf, int cnt);
+};
+
+
+#ifdef RK29_SDMMC_NOTIFY_REMOVE_INSERTION
+static struct rk29_sdmmc *globalSDhost[3];
+#endif
+
+#define rk29_sdmmc_test_and_clear_pending(host, event) \
+ test_and_clear_bit(event, &host->pending_events)
+#define rk29_sdmmc_test_pending(host, event) \
+ test_bit(event, &host->pending_events)
+#define rk29_sdmmc_test_completed(host, event) \
+ test_bit(event, &host->completed_events)
+#define rk29_sdmmc_set_completed(host, event) \
+ set_bit(event, &host->completed_events)
+#define rk29_sdmmc_set_pending(host, event) \
+ set_bit(event, &host->pending_events)
+
+
#endif