* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
+ * mount -t debugfs debugfs /data/debugfs;cat /data/debugfs/mmc0/status
+ * echo 't' >/proc/sysrq-trigger
+ * echo 19 >/sys/module/wakelock/parameters/debug_mask
+ * vdc volume uevent on
*/
#include <linux/blkdev.h>
#include <linux/stat.h>
#include <linux/delay.h>
#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/version.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/card.h>
#include <mach/board.h>
-#include <mach/rk29_iomap.h>
+#include <mach/io.h>
#include <mach/gpio.h>
+#include <mach/iomux.h>
+
#include <asm/dma.h>
-#include <mach/rk29-dma-pl330.h>
+#include <mach/dma-pl330.h>
#include <asm/scatterlist.h>
-#include "rk2818-sdmmc.h"
+#include "rk29_sdmmc.h"
+
+
+#define RK29_SDMMC_xbw_Debug 0
+
+#if RK29_SDMMC_xbw_Debug
+int debug_level = 5;
+#define xbwprintk(n, format, arg...) \
+ if (n <= debug_level) { \
+ printk(format,##arg); \
+ }
+#else
+#define xbwprintk(n, arg...)
+#endif
+
+#define RK29_SDMMC_ERROR_FLAGS (SDMMC_INT_FRUN | SDMMC_INT_HLE )
+
+#if defined(CONFIG_ARCH_RK29)
+#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 )
+
+#elif defined(CONFIG_ARCH_RK30)
+#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 )
+#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.3.03 The last modify date is 2012-03-23,modifyed by XBW."
+
+#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_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | SDMMC_INT_HTO | SDMMC_INT_SBE | SDMMC_INT_EBE | SDMMC_INT_FRUN)
-#define RK29_SDMMC_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | SDMMC_INT_RE | SDMMC_INT_HLE)
-#define RK29_SDMMC_ERROR_FLAGS (RK29_SDMMC_DATA_ERROR_FLAGS | RK29_SDMMC_CMD_ERROR_FLAGS | SDMMC_INT_HLE)
-#define RK29_SDMMC_SEND_STATUS 1
-#define RK29_SDMMC_RECV_STATUS 2
-#define RK29_SDMMC_DMA_THRESHOLD 1
+#define RK29_SDMMC_DEFAULT_SDIO_FREQ 0 // 1--run in default frequency(50Mhz); 0---run in 25Mhz,
+#define RK29_MAX_SDIO_FREQ 25000000 //set max-sdio-frequency 25Mhz at the present time¡£
enum {
EVENT_CMD_COMPLETE = 0,
- EVENT_XFER_COMPLETE,
EVENT_DATA_COMPLETE,
EVENT_DATA_ERROR,
EVENT_XFER_ERROR
enum rk29_sdmmc_state {
STATE_IDLE = 0,
STATE_SENDING_CMD,
- STATE_SENDING_DATA,
STATE_DATA_BUSY,
STATE_SENDING_STOP,
- STATE_DATA_ERROR,
};
-static struct rk29_dma_client rk29_dma_sdmmc0_client = {
- .name = "rk29-dma-sdmmc0",
+struct rk29_sdmmc_dma_info {
+ enum dma_ch chn;
+ char *name;
+ struct rk29_dma_client client;
};
-static struct rk29_dma_client rk29_dma_sdio1_client = {
- .name = "rk29-dma-sdio1",
+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;
+ void __iomem *regs;
struct clk *clk;
- struct scatterlist *sg;
- unsigned int pio_offset;
+
struct mmc_request *mrq;
- struct mmc_request *curr_mrq;
+ struct mmc_request *new_mrq;
struct mmc_command *cmd;
struct mmc_data *data;
- int dma_chn;
+
dma_addr_t dma_addr;;
- //dma_sg_ll_t *sg_cpu;
unsigned int use_dma:1;
char dma_name[8];
u32 cmd_status;
u32 data_status;
u32 stop_cmdr;
- u32 dir_status;
+
+ 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.
+ u32 *pbuf;
+ SDC_INT_INFO_T intInfo;
+ struct rk29_sdmmc_dma_info dma_info;
+
+ 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;
- u32 current_speed;
struct platform_device *pdev;
struct mmc_host *mmc;
u32 ctype;
- struct list_head queue_node;
unsigned int clock;
unsigned long flags;
+
#define RK29_SDMMC_CARD_PRESENT 0
-#define RK29_SDMMC_CARD_NEED_INIT 1
-#define RK29_SDMMC_SHUTDOWN 2
+
int id;
- int irq;
- struct timer_list detect_timer;
- unsigned int oldstatus;
+
+ 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;
+
+ /* flag for current bus settings */
+ u32 bus_mode;
+
+ unsigned int oldstatus;
+ unsigned int complete_done;
+ unsigned int retryfunc;
+
+#ifdef CONFIG_PM
+ int gpio_irq;
+ int gpio_det;
+#endif
+
+#if defined(CONFIG_SDMMC0_RK29_WRITE_PROTECT) || defined(CONFIG_SDMMC1_RK29_WRITE_PROTECT)
+ int write_protect;
+#endif
+
+ 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_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 void rk29_sdmmc_write(unsigned char __iomem *regbase, unsigned int regOff,unsigned int val)
{
__raw_writel(val,regbase + regOff);
return __raw_readl(regbase + regOff);
}
+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("=======printk %s-register end =========\n", host->dma_name);
+ return 0;
+}
+
+
+#ifdef RK29_SDMMC_NOTIFY_REMOVE_INSERTION
+ssize_t rk29_sdmmc_progress_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rk29_sdmmc *host = NULL;
+ static u32 unmounting_times = 0;
+ static char oldbuf[64];
+
+ if( !strncmp(buf,"version" , strlen("version")))
+ {
+ printk("\n The driver SDMMC named 'rk29_sdmmc.c' is %s. ==xbw==\n", RK29_SDMMC_VERSION);
+ return count;
+ }
+
+ //envalue the address of host base on input-parameter.
+ if( !strncmp(buf,"sd-" , strlen("sd-")) )
+ {
+ host = (struct rk29_sdmmc *)globalSDhost[0];
+ if(!host)
+ {
+ printk("%s..%d.. fail to call progress_store because the host is null. ==xbw==\n",__FUNCTION__,__LINE__);
+ return count;
+ }
+ }
+ else if( !strncmp(buf,"sdio1-" , strlen("sdio1-")) )
+ {
+ host = (struct rk29_sdmmc *)globalSDhost[RK29_CTRL_SDIO1_ID];
+ if(!host)
+ {
+ printk("%s..%d.. fail to call progress_store because the host-sdio1 is null. ==xbw==\n",__FUNCTION__,__LINE__);
+ return count;
+ }
+ }
+ else if( !strncmp(buf,"sdio2-" , strlen("sdio2-")) )
+ {
+ host = (struct rk29_sdmmc *)globalSDhost[RK29_CTRL_SDIO2_ID];
+ if(!host)
+ {
+ printk("%s..%d.. fail to call progress_store because the host-sdio2 is null. ==xbw==\n",__FUNCTION__,__LINE__);
+ return count;
+ }
+ }
+ else
+ {
+ printk("%s..%d.. You want to use sysfs for SDMMC but input-parameter is wrong.====xbw====\n",__FUNCTION__,__LINE__);
+ return count;
+ }
+
+ spin_lock(&host->lock);
+
+ if(strncmp(buf,oldbuf , strlen(buf)))
+ {
+ printk(".%d.. MMC0 receive the message %s from VOLD.====xbw[%s]====\n", __LINE__, buf, host->dma_name);
+ strcpy(oldbuf, buf);
+ }
+
+ /*
+ * //deal with the message
+ * insert card state-change: No-Media ==> Pending ==> Idle-Unmounted ==> Checking ==>Mounted
+ * remove card state-change: Unmounting ==> Idle-Unmounted ==> No-Media
+ */
+ #if !defined(CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD)
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+#if 1 //to wirte log in log-file-system during the stage of umount. Modifyed by xbw at 2011-12-26
+ if(!strncmp(buf, "sd-Unmounting", strlen("sd-Unmounting")))
+ {
+ if(unmounting_times++%10 == 0)
+ {
+ printk(".%d.. MMC0 receive the message Unmounting(waitTimes=%d) from VOLD.====xbw[%s]====\n", \
+ __LINE__, unmounting_times, host->dma_name);
+ }
+
+ if(0 == host->mmc->re_initialized_flags)
+ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_REMOVAL_DELAY*2));
+ }
+ else if(!strncmp(buf, "sd-Idle-Unmounted", strlen("sd-Idle-Unmounted")))
+ {
+ if(0 == host->mmc->re_initialized_flags)
+ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_REMOVAL_DELAY*2));
+ }
+#else
+ if(!strncmp(buf, "sd-Unmounting", strlen("sd-Unmounting")))
+ {
+ if(unmounting_times++%10 == 0)
+ {
+ printk(".%d.. MMC0 receive the message Unmounting(waitTimes=%d) from VOLD.====xbw[%s]====\n", \
+ __LINE__, unmounting_times, host->dma_name);
+ }
+ host->mmc->re_initialized_flags = 0;
+ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_REMOVAL_DELAY*2));
+ }
+#endif
+ else if( !strncmp(buf, "sd-No-Media", strlen("sd-No-Media")))
+ {
+ printk(".%d.. MMC0 receive the message No-Media from VOLD. waitTimes=%d ====xbw[%s]====\n" ,\
+ __LINE__,unmounting_times, host->dma_name);
+
+ del_timer_sync(&host->detect_timer);
+ host->mmc->re_initialized_flags = 1;
+ unmounting_times = 0;
+
+ if(test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags))
+ {
+ mmc_detect_change(host->mmc, 0);
+ }
+ }
+ else if( !strncmp(buf, "sd-Ready", strlen("sd-Ready")))
+ {
+ printk(".%d.. MMC0 receive the message Ready(ReInitFlag=%d) from VOLD. waitTimes=%d====xbw[%s]====\n" ,\
+ __LINE__, host->mmc->re_initialized_flags, unmounting_times, host->dma_name);
+
+ unmounting_times = 0;
+ host->mmc->re_initialized_flags = 1;
+ }
+ else if( !strncmp(buf,"sd-reset" , strlen("sd-reset")) )
+ {
+ printk(".%d.. Now manual reset for SDMMC0. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_hw_init(host);
+ mmc_detect_change(host->mmc, 0);
+ }
+ else if( !strncmp(buf, "sd-regs", strlen("sd-regs")))
+ {
+ printk(".%d.. Now printk the register of SDMMC0. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_regs_printk(host);
+ }
+
+ }
+ #else
+ if(0 == host->pdev->id)
+ {
+ if( !strncmp(buf,"sd-reset" , strlen("sd-reset")) )
+ {
+ printk(".%d.. Now manual reset for SDMMC0. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_hw_init(host);
+ mmc_detect_change(host->mmc, 0);
+ }
+ else if( !strncmp(buf, "sd-regs", strlen("sd-regs")))
+ {
+ printk(".%d.. Now printk the register of SDMMC0. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_regs_printk(host);
+ }
+ }
+ #endif
+ else if(RK29_CTRL_SDIO1_ID == host->pdev->id)
+ {
+ if( !strncmp(buf, "sdio1-regs", strlen("sdio1-regs")))
+ {
+ printk(".%d.. Now printk the register of SDMMC1. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_regs_printk(host);
+ }
+ else if( !strncmp(buf,"sdio1-reset" , strlen("sdio1-reset")) )
+ {
+ printk(".%d.. Now manual reset for SDMMC1. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_hw_init(host);
+ mmc_detect_change(host->mmc, 0);
+ }
+ }
+ else if(RK29_CTRL_SDIO2_ID == host->pdev->id)
+ {
+ if( !strncmp(buf, "sdio2-regs", strlen("sdio2-regs")))
+ {
+ printk(".%d.. Now printk the register of SDMMC2. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_regs_printk(host);
+ }
+ else if( !strncmp(buf,"sdio2-reset" , strlen("sdio2-reset")) )
+ {
+ printk(".%d.. Now manual reset for SDMMC2. ====xbw[%s]====\n",__LINE__, host->dma_name);
+ rk29_sdmmc_hw_init(host);
+ mmc_detect_change(host->mmc, 0);
+ }
+ }
+
+ spin_unlock(&host->lock);
+
+ return count;
+}
+
+
+
+struct kobj_attribute mmc_reset_attrs =
+{
+ .attr = {
+ .name = "rescan",
+ .mode = 0764},
+ .show = NULL,
+ .store = rk29_sdmmc_progress_store,
+};
+struct attribute *mmc_attrs[] =
+{
+ &mmc_reset_attrs.attr,
+ NULL
+};
+
+static struct kobj_type mmc_kset_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_attrs = &mmc_attrs[0],
+};
+
+static int rk29_sdmmc_progress_add_attr( struct platform_device *pdev )
+{
+ int result;
+ 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 );
+
+ parentkobject = &pdev->dev.kobj ;
+ result = kobject_add( me , parentkobject->parent->parent->parent,"%s", "sd-sdio" );
+
+ return result;
+}
+#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.
.release = single_release,
};
-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;
-}
static int rk29_sdmmc_regs_open(struct inode *inode, struct file *file)
{
}
#endif
-static inline unsigned ns_to_clocks(unsigned clkrate, unsigned ns)
+
+static u32 rk29_sdmmc_prepare_command(struct mmc_command *cmd)
{
- u32 clks;
- if (clkrate > 1000000)
- clks = (ns * (clkrate / 1000000) + 999) / 1000;
- else
- clks = ((ns/1000) * (clkrate / 1000) + 999) / 1000;
+ u32 cmdr = cmd->opcode;
- return clks;
-}
+ switch(cmdr)
+ {
+ 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_SEND_STATUS:
+ case MMC_GO_INACTIVE_STATE:
+ cmdr |= SDMMC_CMD_PRV_DAT_NO_WAIT;
+ break;
+
+ default:
+ cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+ break;
+ }
-static void rk29_sdmmc_set_timeout(struct rk29_sdmmc *host,struct mmc_data *data)
-{
- unsigned timeout;
+ /* response type */
+ switch(mmc_resp_type(cmd))
+ {
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ // case MMC_RSP_R5: //R5,R6,R7 is same with the R1
+ //case MMC_RSP_R6:
+ // case R6m_TYPE:
+ // case MMC_RSP_R7:
+ cmdr |= (SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_SHORT | SDMMC_CMD_RESP_EXP);
+ break;
+ case MMC_RSP_R3:
+ //case MMC_RSP_R4:
+ /* these response not contain crc7, so don't care crc error and response error */
+ cmdr |= (SDMMC_CMD_RESP_NO_CRC | SDMMC_CMD_RESP_SHORT | SDMMC_CMD_RESP_EXP);
+ break;
+ case MMC_RSP_R2:
+ cmdr |= (SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_LONG | SDMMC_CMD_RESP_EXP);
+ break;
+ case MMC_RSP_NONE:
+ cmdr |= (SDMMC_CMD_RESP_CRC_NOCARE | SDMMC_CMD_RESP_NOCARE | SDMMC_CMD_RESP_NO_EXP);
+ break;
+ default:
+ cmdr |= (SDMMC_CMD_RESP_CRC_NOCARE | SDMMC_CMD_RESP_NOCARE | SDMMC_CMD_RESP_NO_EXP);
+ break;
+ }
- timeout = ns_to_clocks(host->clock, data->timeout_ns) + data->timeout_clks;
- rk29_sdmmc_write(host->regs, SDMMC_TMOUT, 0xffffffff);
- ///rk29_sdmmc_write(host->regs, SDMMC_TMOUT, (timeout << 8) | (70));
+ return cmdr;
}
-static u32 rk29_sdmmc_prepare_command(struct mmc_host *mmc,
- struct mmc_command *cmd)
+void rk29_sdmmc_set_frq(struct rk29_sdmmc *host)
{
- struct mmc_data *data;
- u32 cmdr;
-
- cmd->error = -EINPROGRESS;
- cmdr = cmd->opcode;
+ struct mmc_host *mmchost = platform_get_drvdata(host->pdev);
+ struct mmc_card *card;
+ struct mmc_ios *ios;
+ unsigned int max_dtr;
+
+ extern void mmc_set_clock(struct mmc_host *host, unsigned int hz);
- if(cmdr == 12)
- cmdr |= SDMMC_CMD_STOP;
- else if(cmdr == 13)
- cmdr &= ~SDMMC_CMD_PRV_DAT_WAIT;
- else
- cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+ if(!mmchost)
+ return;
- if (cmd->flags & MMC_RSP_PRESENT) {
- cmdr |= SDMMC_CMD_RESP_EXP; // expect the respond, need to set this bit
- if (cmd->flags & MMC_RSP_136)
- cmdr |= SDMMC_CMD_RESP_LONG; // expect long respond
-
- if(cmd->flags & MMC_RSP_CRC)
- cmdr |= SDMMC_CMD_RESP_CRC;
- }
+ card = (struct mmc_card *)mmchost->card;
+ ios = ( struct mmc_ios *)&mmchost->ios;
+
+ if(!card || !ios)
+ return;
+
+ if(MMC_POWER_ON == ios->power_mode)
+ return;
+
+ max_dtr = (unsigned int)-1;
+
+ if (mmc_card_highspeed(card))
+ {
+ if (max_dtr > card->ext_csd.hs_max_dtr)
+ max_dtr = card->ext_csd.hs_max_dtr;
+
+ }
+ else if (max_dtr > card->csd.max_dtr)
+ {
+ if(MMC_TYPE_SD == card->type)
+ {
+ max_dtr = (card->csd.max_dtr > SD_FPP_FREQ) ? SD_FPP_FREQ : (card->csd.max_dtr);
+ }
+ else
+ {
+ max_dtr = (card->csd.max_dtr > MMC_FPP_FREQ) ? MMC_FPP_FREQ : (card->csd.max_dtr);
+ }
+ }
+
+ xbwprintk(7, "%s..%d... call mmc_set_clock() set clk=%d ===xbw[%s]===\n", \
+ __FUNCTION__, __LINE__, max_dtr, host->dma_name);
+
+
+ mmc_set_clock(mmchost, max_dtr);
- data = cmd->data;
- if (data) {
- cmdr |= SDMMC_CMD_DAT_EXP;
- if (data->flags & MMC_DATA_STREAM)
- cmdr |= SDMMC_CMD_STRM_MODE; // set stream mode
- if (data->flags & MMC_DATA_WRITE)
- cmdr |= SDMMC_CMD_DAT_WR;
- }
- return cmdr;
}
-static void rk29_sdmmc_start_command(struct rk29_sdmmc *host,
- struct mmc_command *cmd, u32 cmd_flags)
+static int rk29_sdmmc_start_command(struct rk29_sdmmc *host, struct mmc_command *cmd, u32 cmd_flags)
{
- int tmo = 5000;
- unsigned long flags;
+ int tmo = RK29_SDMMC_SEND_START_TIMEOUT*10;//wait 60ms cycle.
+
host->cmd = cmd;
- dev_vdbg(&host->pdev->dev,
- "start cmd:%d ARGR=0x%08x CMDR=0x%08x\n",
- cmd->opcode, cmd->arg, cmd_flags);
- local_irq_save(flags);
+ host->old_cmd = cmd->opcode;
+ host->errorstep = 0;
+ host->pending_events = 0;
+ host->completed_events = 0;
+ host->complete_done = 0;
+ host->retryfunc = 0;
+ host->cmd_status = 0;
+
+ if(MMC_STOP_TRANSMISSION != cmd->opcode)
+ {
+ host->data_status = 0;
+ }
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ //adjust the frequency division control of SDMMC0 every time.
+ rk29_sdmmc_set_frq(host);
+ }
+
rk29_sdmmc_write(host->regs, SDMMC_CMDARG, cmd->arg); // write to SDMMC_CMDARG register
rk29_sdmmc_write(host->regs, SDMMC_CMD, cmd_flags | SDMMC_CMD_START); // write to SDMMC_CMD register
- local_irq_restore(flags);
+
+
+ xbwprintk(5, "\n%s..%d..************.start cmd=%d, arg=0x%x ********=====xbw[%s]=======\n", \
+ __FUNCTION__, __LINE__, cmd->opcode, cmd->arg, host->dma_name);
+
+ host->mmc->doneflag = 1;
/* wait until CIU accepts the command */
- while (--tmo && (rk29_sdmmc_read(host->regs, SDMMC_CMD) & SDMMC_CMD_START))
- cpu_relax();
- if(!tmo){
- dev_info(&host->pdev->dev, "Enter:%s %d start tmo err!!\n",__FUNCTION__,__LINE__);
+ while (--tmo && (rk29_sdmmc_read(host->regs, SDMMC_CMD) & SDMMC_CMD_START))
+ {
+ udelay(2);//cpu_relax();
}
-}
+
+ if(!tmo)
+ {
+ if(0==cmd->retries)
+ {
+ printk("%s..%d.. CMD_START timeout! CMD%d(arg=0x%x, retries=%d) ======xbw[%s]======\n",\
+ __FUNCTION__,__LINE__, cmd->opcode, cmd->arg, cmd->retries,host->dma_name);
+ }
-static void rk29_sdmmc_reset_fifo(struct rk29_sdmmc *host)
-{
- unsigned long flags;
- return 0;
- dev_info(&host->pdev->dev, "reset fifo\n");
- local_irq_save(flags);
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, rk29_sdmmc_read(host->regs, SDMMC_CTRL) | SDMMC_CTRL_FIFO_RESET);
- /* wait till resets clear */
- while (rk29_sdmmc_read(host->regs, SDMMC_CTRL) & SDMMC_CTRL_FIFO_RESET);
- local_irq_restore(flags);
+ cmd->error = -ETIMEDOUT;
+ host->mrq->cmd->error = -ETIMEDOUT;
+ del_timer_sync(&host->request_timer);
+
+ host->errorstep = 0x1;
+ return SDM_WAIT_FOR_CMDSTART_TIMEOUT;
+ }
+ host->errorstep = 0xfe;
+
+ return SDM_SUCCESS;
}
-static int rk29_sdmmc_wait_unbusy(struct rk29_sdmmc *host)
+static int rk29_sdmmc_reset_fifo(struct rk29_sdmmc *host)
{
- const int time_out_us = 500000;
- int time_out = time_out_us, time_out2 = 3;
- return 0;
- while (rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_DATA_BUSY) {
- udelay(1);
- time_out--;
- if (!time_out) {
- time_out = time_out_us;
- rk29_sdmmc_reset_fifo(host);
- if (!time_out2)
- break;
- time_out2--;
- }
- }
-
- return time_out_us - time_out;
+ u32 value;
+ int timeout;
+ int ret = SDM_SUCCESS;
+
+ value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
+ if (!(value & SDMMC_STAUTS_FIFO_EMPTY))
+ {
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+ value |= SDMMC_CTRL_FIFO_RESET;
+ rk29_sdmmc_write(host->regs, SDMMC_CTRL, value);
+
+ timeout = 1000;
+ while (((value = rk29_sdmmc_read(host->regs, SDMMC_CTRL)) & (SDMMC_CTRL_FIFO_RESET)) && (timeout > 0))
+ {
+ udelay(1);
+ timeout--;
+ }
+ if (timeout == 0)
+ {
+ host->errorstep = 0x2;
+ ret = SDM_WAIT_FOR_FIFORESET_TIMEOUT;
+ }
+ }
+
+ return ret;
+
}
-static void send_stop_cmd(struct rk29_sdmmc *host, struct mmc_data *data)
+static int rk29_sdmmc_wait_unbusy(struct rk29_sdmmc *host)
{
- rk29_sdmmc_wait_unbusy(host);
+ int time_out = 500000;//250000; //max is 250ms; //adapt the value to the sick card. modify at 2011-10-08
+
+ while (rk29_sdmmc_read(host->regs, SDMMC_STATUS) & (SDMMC_STAUTS_DATA_BUSY|SDMMC_STAUTS_MC_BUSY))
+ {
+ udelay(1);
+ time_out--;
- if(!(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_EMPTY)) {
- rk29_sdmmc_reset_fifo(host);
+ if(time_out == 0)
+ {
+ host->errorstep = 0x3;
+ return SDM_BUSY_TIMEOUT;
+ }
}
- rk29_sdmmc_start_command(host, data->stop, host->stop_cmdr);
+
+ return SDM_SUCCESS;
}
static void rk29_sdmmc_dma_cleanup(struct rk29_sdmmc *host)
{
- struct mmc_data *data = host->data;
- if (data)
- dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
- ((data->flags & MMC_DATA_WRITE)
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ if (host->data)
+ {
+ dma_unmap_sg(&host->pdev->dev, host->data->sg, host->data->sg_len,
+ ((host->data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ }
}
static void rk29_sdmmc_stop_dma(struct rk29_sdmmc *host)
{
+ int ret = 0;
+
if(host->use_dma == 0)
return;
- if (host->dma_chn > 0) {
- //dma_stop_channel(host->dma_chn);
- rk29_dma_ctrl(host->dma_chn,RK29_DMAOP_STOP);
- rk29_sdmmc_dma_cleanup(host);
- } else {
+
+ if (host->dma_info.chn> 0)
+ {
+ rk29_sdmmc_dma_cleanup(host);
+
+ ret = rk29_dma_ctrl(host->dma_info.chn,RK29_DMAOP_STOP);
+ if(ret < 0)
+ {
+ printk("%s..%d...rk29_dma_ctrl STOP error!===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x95;
+ return;
+ }
+
+ ret = rk29_dma_ctrl(host->dma_info.chn,RK29_DMAOP_FLUSH);
+ if(ret < 0)
+ {
+ printk("%s..%d...rk29_dma_ctrl FLUSH error!===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x96;
+ return;
+ }
+
+ }
+ else
+ {
/* Data transfer was stopped by the interrupt handler */
- rk29_sdmmc_set_pending(host, EVENT_XFER_COMPLETE);
+ rk29_sdmmc_set_pending(host, EVENT_DATA_COMPLETE);
}
}
+static void rk29_sdmmc_control_host_dma(struct rk29_sdmmc *host, bool enable)
+{
+ 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;
+
+ if(host->mrq->cmd->error)
+ {
+ //stop DMA
+ if(host->dodma)
+ {
+ rk29_sdmmc_stop_dma(host);
+ rk29_sdmmc_control_host_dma(host, FALSE);
+
+ host->dodma = 0;
+ }
+
+ ret= rk29_sdmmc_clear_fifo(host);
+ if(SDM_SUCCESS != ret)
+ {
+ xbwprintk(3, "%s..%d.. clear fifo error before call CMD_STOP ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->dma_name);
+ }
+ }
+
+ mod_timer(&host->request_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_SEND_START_TIMEOUT+1500));
+
+ host->stopcmd.opcode = MMC_STOP_TRANSMISSION;
+ host->stopcmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;;
+ host->stopcmd.arg = 0;
+ host->stopcmd.data = NULL;
+ host->stopcmd.mrq = NULL;
+ host->stopcmd.retries = 0;
+ host->stopcmd.error = 0;
+ if(host->mrq && host->mrq->stop)
+ {
+ host->mrq->stop->error = 0;
+ }
+
+ host->cmdr = rk29_sdmmc_prepare_command(&host->stopcmd);
+
+ ret = rk29_sdmmc_start_command(host, &host->stopcmd, host->cmdr);
+ if(SDM_SUCCESS != ret)
+ {
+ rk29_sdmmc_start_error(host);
+
+ host->state = STATE_IDLE;
+ host->complete_done = 4;
+ }
+}
+
+
/* This function is called by the DMA driver from tasklet context. */
-static void rk29_sdmmc_dma_complete(void *arg, int size, enum rk29_dma_buffresult result) ///(int chn, dma_irq_type_t type, void *arg)
+static void rk29_sdmmc_dma_complete(void *arg, int size, enum rk29_dma_buffresult result)
{
struct rk29_sdmmc *host = arg;
- struct mmc_data *data = host->data;
if(host->use_dma == 0)
return;
- dev_vdbg(&host->pdev->dev, "DMA complete\n");
- spin_lock(&host->lock);
- rk29_sdmmc_dma_cleanup(host);
- /*
- * If the card was removed, data will be NULL. No point trying
- * to send the stop command or waiting for NBUSY in this case.
- */
- if (data) {
- rk29_sdmmc_set_pending(host, EVENT_XFER_COMPLETE);
- tasklet_schedule(&host->tasklet);
- }
- spin_unlock(&host->lock);
- if(result != RK29_RES_OK){
- rk29_dma_ctrl(host->dma_chn,RK29_DMAOP_STOP);
- rk29_dma_ctrl(host->dma_chn,RK29_DMAOP_FLUSH);
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (rk29_sdmmc_read(host->regs, SDMMC_CTRL))&(~SDMMC_CTRL_DMA_ENABLE));
- printk(KERN_DEBUG "%s: sdio dma complete err\n",__FUNCTION__);
- }
+
+ host->intInfo.transLen = host->intInfo.desLen;
}
static int rk29_sdmmc_submit_data_dma(struct rk29_sdmmc *host, struct mmc_data *data)
{
struct scatterlist *sg;
- unsigned int i,direction;
- int dma_len=0;
+ unsigned int i,direction, sgDirection;
+ int ret, dma_len=0;
if(host->use_dma == 0)
- return -ENOSYS;
- /* If we don't have a channel, we can't do DMA */
- if (host->dma_chn < 0)
- return -ENODEV;
-
- /*
- * We don't do DMA on "complex" transfers, i.e. with
- * non-word-aligned buffers or lengths. Also, we don't bother
- * with all the DMA setup overhead for short transfers.
- */
- if (data->blocks * data->blksz < RK29_SDMMC_DMA_THRESHOLD)
{
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,rk29_sdmmc_read(host->regs,SDMMC_INTMASK) | SDMMC_INT_TXDR | SDMMC_INT_RXDR );
- return -EINVAL;
+ printk("%s..%d...setup DMA fail!!!!!!. host->use_dma=0 ===xbw=[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x4;
+ return -ENOSYS;
}
- else
+ /* If we don't have a channel, we can't do DMA */
+ if (host->dma_info.chn < 0)
{
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,rk29_sdmmc_read(host->regs,SDMMC_INTMASK) & (~( SDMMC_INT_TXDR | SDMMC_INT_RXDR)));
+ printk("%s..%d...setup DMA fail!!!!!!!. dma_info.chn < 0 ===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x5;
+ return -ENODEV;
}
+
if (data->blksz & 3)
return -EINVAL;
- for_each_sg(data->sg, sg, data->sg_len, i) {
+ for_each_sg(data->sg, sg, data->sg_len, i)
+ {
if (sg->offset & 3 || sg->length & 3)
+ {
+ printk("%s..%d...call for_each_sg() fail !!===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x7;
return -EINVAL;
+ }
}
if (data->flags & MMC_DATA_READ)
+ {
direction = RK29_DMASRC_HW;
+ sgDirection = DMA_FROM_DEVICE;
+ }
else
- direction = RK29_DMASRC_MEM;
- if(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_FULL ) {
- rk29_sdmmc_reset_fifo(host);
- dev_info(&host->pdev->dev, "%s %d fifo full reset\n",__FUNCTION__,__LINE__);
- }
- rk29_dma_ctrl(host->dma_chn,RK29_DMAOP_STOP);
- rk29_dma_ctrl(host->dma_chn,RK29_DMAOP_FLUSH);
- rk29_dma_devconfig(host->dma_chn, direction, (unsigned long )(host->dma_addr));
- dma_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
- (data->flags & MMC_DATA_READ)? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- for (i = 0; i < dma_len; i++)
- rk29_dma_enqueue(host->dma_chn, host, sg_dma_address(&data->sg[i]),sg_dma_len(&data->sg[i])); // data->sg->dma_address, data->sg->length);
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (rk29_sdmmc_read(host->regs, SDMMC_CTRL))|SDMMC_CTRL_DMA_ENABLE);// enable dma
- rk29_dma_ctrl(host->dma_chn, RK29_DMAOP_START);
+ {
+ direction = RK29_DMASRC_MEM;
+ sgDirection = DMA_TO_DEVICE;
+ }
+
+ ret = rk29_dma_ctrl(host->dma_info.chn,RK29_DMAOP_STOP);
+ if(ret < 0)
+ {
+ printk("%s..%d...rk29_dma_ctrl stop error!===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x91;
+ return -ENOSYS;
+ }
+
+ ret = rk29_dma_ctrl(host->dma_info.chn,RK29_DMAOP_FLUSH);
+ if(ret < 0)
+ {
+ printk("%s..%d...rk29_dma_ctrl flush error!===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x91;
+ return -ENOSYS;
+ }
+
+
+ ret = rk29_dma_devconfig(host->dma_info.chn, direction, (unsigned long )(host->dma_addr));
+ if(0 != ret)
+ {
+ printk("%s..%d...call rk29_dma_devconfig() fail !!!!===xbw=[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x8;
+ return -ENOSYS;
+ }
+
+ dma_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, sgDirection);
+ for (i = 0; i < dma_len; i++)
+ {
+ ret = rk29_dma_enqueue(host->dma_info.chn, host, sg_dma_address(&data->sg[i]),sg_dma_len(&data->sg[i]));
+ if(ret < 0)
+ {
+ printk("%s..%d...call rk29_dma_devconfig() fail !!!!===xbw=[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x93;
+ return -ENOSYS;
+ }
+ }
+
+ rk29_sdmmc_control_host_dma(host, TRUE);// enable dma
+ ret = rk29_dma_ctrl(host->dma_info.chn, RK29_DMAOP_START);
+ if(ret < 0)
+ {
+ printk("%s..%d...rk29_dma_ctrl start error!===xbw[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ host->errorstep = 0x94;
+ return -ENOSYS;
+ }
+
return 0;
}
-static void rk29_sdmmc_submit_data(struct rk29_sdmmc *host, struct mmc_data *data)
+
+static int rk29_sdmmc_prepare_write_data(struct rk29_sdmmc *host, struct mmc_data *data)
+{
+ //uint32 value;
+ int output;
+ u32 i = 0;
+ u32 dataLen;
+ u32 count, *pBuf = (u32 *)host->pbuf;
+
+ output = SDM_SUCCESS;
+ dataLen = data->blocks*data->blksz;
+
+ host->dodma = 0; //DMA still no request;
+
+ //SDMMC controller request the data is multiple of 4.
+ count = (dataLen >> 2) + ((dataLen & 0x3) ? 1:0);
+
+ if (count <= FIFO_DEPTH)
+ {
+ for (i=0; i<count; i++)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_DATA, pBuf[i]);
+ }
+ }
+ else
+ {
+ host->intInfo.desLen = count;
+ host->intInfo.transLen = 0;
+ host->intInfo.pBuf = (u32 *)pBuf;
+
+ if(0)//(host->intInfo.desLen <= 512 )
+ {
+ //use pio-mode
+ return SDM_SUCCESS;
+ }
+ else
+ {
+ xbwprintk(7, "%s..%d... trace data, ======xbw=[%s]====\n", __FUNCTION__, __LINE__, host->dma_name);
+ output = rk29_sdmmc_submit_data_dma(host, data);
+ if(output)
+ {
+ host->dodma = 0;
+
+ printk("%s..%d... CMD%d setupDMA failure!!!!! pre_cmd=%d ==xbw[%s]==\n", \
+ __FUNCTION__, __LINE__, host->cmd->opcode,host->old_cmd, host->dma_name);
+
+ host->errorstep = 0x81;
+
+ rk29_sdmmc_control_host_dma(host, FALSE);
+ }
+ else
+ {
+ host->dodma = 1;
+ }
+ }
+
+ }
+
+ return output;
+}
+
+
+
+
+static int rk29_sdmmc_prepare_read_data(struct rk29_sdmmc *host, struct mmc_data *data)
{
- data->error = -EINPROGRESS;
+ u32 count = 0;
+ u32 dataLen;
+ int output;
- WARN_ON(host->data);
- host->sg = NULL;
- host->data = data;
+ output = SDM_SUCCESS;
+ dataLen = data->blocks*data->blksz;
+
+ host->dodma = 0;//DMA still no request;
- if (rk29_sdmmc_submit_data_dma(host, data)) {
- host->sg = data->sg;
- host->pio_offset = 0;
- if (data->flags & MMC_DATA_READ)
- host->dir_status = RK29_SDMMC_RECV_STATUS;
- else
- host->dir_status = RK29_SDMMC_SEND_STATUS;
+ //SDMMC controller request the data is multiple of 4.
+ count = (dataLen >> 2) ;//+ ((dataLen & 0x3) ? 1:0);
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (rk29_sdmmc_read(host->regs, SDMMC_CTRL))&(~SDMMC_CTRL_DMA_ENABLE));
- }
+ host->intInfo.desLen = (dataLen >> 2);
+ host->intInfo.transLen = 0;
+ host->intInfo.pBuf = (u32 *)host->pbuf;
+
+ 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 )
+ {
+ //use pio-mode
+ return SDM_SUCCESS;
+ }
+ else
+ {
+ output = rk29_sdmmc_submit_data_dma(host, data);
+ if(output)
+ {
+ host->dodma = 0;
+
+ printk("%s..%d... CMD%d setupDMA failure!!! ==xbw[%s]==\n", \
+ __FUNCTION__, __LINE__, host->cmd->opcode, host->dma_name);
+
+ host->errorstep = 0x82;
+
+ rk29_sdmmc_control_host_dma(host, FALSE);
+ }
+ else
+ {
+ host->dodma = 1;
+ }
+ }
+ }
+
+ return output;
+}
+
+
+
+static int rk29_sdmmc_read_remain_data(struct rk29_sdmmc *host, u32 originalLen, void *pDataBuf)
+{
+ u32 value = 0;
+
+ u32 i = 0;
+ u32 *pBuf = (u32 *)pDataBuf;
+ u8 *pByteBuf = (u8 *)pDataBuf;
+ u32 lastData = 0;
+
+ //SDMMC controller must be multiple of 32. so if transfer 13, then actuall we should write or read 16 byte.
+ u32 count = (originalLen >> 2) + ((originalLen & 0x3) ? 1:0);
+
+ if(1 == host->dodma)
+ {
+ //when use DMA, there are remain data only when datalen/4 less than RX_WMARK+1 same as equaltion. or not multiple of 4
+ if (!((value = rk29_sdmmc_read(host->regs, SDMMC_STATUS)) & SDMMC_STAUTS_FIFO_EMPTY))
+ {
+ if (count <= (RX_WMARK+1))
+ {
+ i = 0;
+ while ((i<(originalLen >> 2))&&(!(value & SDMMC_STAUTS_FIFO_EMPTY)))
+ {
+ pBuf[i++] = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+ value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
+ }
+ }
+
+ if (count > (originalLen >> 2))
+ {
+ lastData = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+
+ //fill the 1 to 3 byte.
+ for (i=0; i<(originalLen & 0x3); i++)
+ {
+ pByteBuf[(originalLen & 0xFFFFFFFC) + i] = (u8)((lastData >> (i << 3)) & 0xFF); //default little-endian
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!((value = rk29_sdmmc_read(host->regs, SDMMC_STATUS)) & SDMMC_STAUTS_FIFO_EMPTY))
+ {
+ while ( (host->intInfo.transLen < host->intInfo.desLen) && (!(value & SDMMC_STAUTS_FIFO_EMPTY)) )
+ {
+ pBuf[host->intInfo.transLen++] = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+ value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
+ }
+
+ if (count > (originalLen >> 2))
+ {
+ lastData = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+
+ //fill the 1 to 3 byte.
+ for (i=0; i<(originalLen & 0x3); i++)
+ {
+ pByteBuf[(originalLen & 0xFFFFFFFC) + i] = (u8)((lastData >> (i << 3)) & 0xFF); //default little-endian
+ }
+ }
+ }
+ }
+
+ return SDM_SUCCESS;
+}
+
+
+static void rk29_sdmmc_do_pio_read(struct rk29_sdmmc *host)
+{
+ int i;
+ for (i=0; i<(RX_WMARK+1); i++)
+ {
+ host->intInfo.pBuf[host->intInfo.transLen + i] = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+ }
+ host->intInfo.transLen += (RX_WMARK+1);
+}
+static void rk29_sdmmc_do_pio_write(struct rk29_sdmmc *host)
+{
+ int i;
+ if ( (host->intInfo.desLen - host->intInfo.transLen) > (FIFO_DEPTH - TX_WMARK) )
+ {
+ for (i=0; i<(FIFO_DEPTH - TX_WMARK); i++)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_DATA, host->intInfo.pBuf[host->intInfo.transLen + i]);
+ }
+ host->intInfo.transLen += (FIFO_DEPTH - TX_WMARK);
+ }
+ else
+ {
+ for (i=0; i<(host->intInfo.desLen - host->intInfo.transLen); i++)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_DATA, host->intInfo.pBuf[host->intInfo.transLen + i]);
+ }
+ host->intInfo.transLen = host->intInfo.desLen;
+ }
+
}
-static void sdmmc_send_cmd(struct rk29_sdmmc *host, unsigned int cmd, int arg)
+
+static void rk29_sdmmc_submit_data(struct rk29_sdmmc *host, struct mmc_data *data)
{
- int tmo = 10000;
+ int ret;
+
+ if(data)
+ {
+ host->data = data;
+ data->error = 0;
+ host->cmd->data = data;
+
+ data->bytes_xfered = 0;
+ host->pbuf = (u32*)sg_virt(data->sg);
+
+ if (data->flags & MMC_DATA_STREAM)
+ {
+ host->cmdr |= SDMMC_CMD_STRM_MODE; //set stream mode
+ }
+ else
+ {
+ host->cmdr |= SDMMC_CMD_BLOCK_MODE; //set block mode
+ }
+
+ //set the blocks and blocksize
+ rk29_sdmmc_write(host->regs, SDMMC_BYTCNT,data->blksz*data->blocks);
+ rk29_sdmmc_write(host->regs, SDMMC_BLKSIZ,data->blksz);
+
+ xbwprintk(6, "%s..%d..CMD%d(arg=0x%x), data->blksz=%d, data->blocks=%d ==xbw=[%s]==\n", \
+ __FUNCTION__, __LINE__, host->cmd->opcode,host->cmd->arg,data->blksz, data->blocks, host->dma_name);
+
+ if (data->flags & MMC_DATA_WRITE)
+ {
+ host->cmdr |= (SDMMC_CMD_DAT_WRITE | SDMMC_CMD_DAT_EXP);
+ xbwprintk(7, "%s..%d... write data, len=%d ======xbw=[%s]====\n", \
+ __FUNCTION__, __LINE__, data->blksz*data->blocks, host->dma_name);
+
+ ret = rk29_sdmmc_prepare_write_data(host, data);
+ }
+ else
+ {
+ host->cmdr |= (SDMMC_CMD_DAT_READ | SDMMC_CMD_DAT_EXP);
+ xbwprintk(7, "%s..%d... read data len=%d ======xbw=[%s]====\n", \
+ __FUNCTION__, __LINE__, data->blksz*data->blocks, host->dma_name);
+
+ ret = rk29_sdmmc_prepare_read_data(host, data);
+ }
+
+ }
+ else
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_BLKSIZ, 0);
+ rk29_sdmmc_write(host->regs, SDMMC_BYTCNT, 0);
+ }
+}
+
+
+static int sdmmc_send_cmd_start(struct rk29_sdmmc *host, unsigned int cmd)
+{
+ int tmo = RK29_SDMMC_SEND_START_TIMEOUT*10;//wait 60ms cycle.
- rk29_sdmmc_write(host->regs, SDMMC_CMDARG, arg);
rk29_sdmmc_write(host->regs, SDMMC_CMD, SDMMC_CMD_START | cmd);
- while (readl(host->regs + SDMMC_CMD) & SDMMC_CMD_START){
- tmo--;
- if(!tmo) {
- tmo = 10000;
- dev_info(&host->pdev->dev, "set cmd register timeout error,status = 0x%08x!!!\n",
- rk29_sdmmc_read(host->regs, SDMMC_STATUS));
- }
+ while (--tmo && (rk29_sdmmc_read(host->regs, SDMMC_CMD) & SDMMC_CMD_START))
+ {
+ udelay(2);
}
+
+ if(!tmo)
+ {
+ printk("%s.. %d set cmd(value=0x%x) register timeout error ! ====xbw[%s]====\n",\
+ __FUNCTION__,__LINE__, cmd, host->dma_name);
+
+ host->errorstep = 0x9;
+ return SDM_START_CMD_FAIL;
+ }
+
+ return SDM_SUCCESS;
}
-void rk29_sdmmc_setup_bus(struct rk29_sdmmc *host)
+static int rk29_sdmmc_get_cd(struct mmc_host *mmc)
{
- u32 div;
+ struct rk29_sdmmc *host = mmc_priv(mmc);
+ u32 cdetect=1;
- if (host->clock != host->current_speed) {
+ switch(host->pdev->id)
+ {
+ case 0:
+ {
+ #ifdef CONFIG_PM
+ if(host->gpio_det == INVALID_GPIO)
+ return 1;
+ #endif
+ cdetect = rk29_sdmmc_read(host->regs, SDMMC_CDETECT);
- while(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & (SDMMC_STAUTS_MC_BUSY|SDMMC_STAUTS_DATA_BUSY)){
- /* reset all blocks */
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- /* wait till resets clear */
- dev_info(&host->pdev->dev, "sdmmc_send cmd mci busy, ctrl reset ");
- while (rk29_sdmmc_read(host->regs, SDMMC_CTRL) & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- printk("done\n");
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, rk29_sdmmc_read(host->regs, SDMMC_CTRL) | SDMMC_CTRL_INT_ENABLE);
+ cdetect = (cdetect & SDMMC_CARD_DETECT_N)?0:1;
+
+ break;
+ }
+
+ case 1:
+ {
+ #if defined(CONFIG_USE_SDMMC1_FOR_WIFI_DEVELOP_BOARD)
+ cdetect = 1;
+ #else
+ cdetect = test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags)?1:0;
+ #endif
+ break;
+ }
+
+ default:
+ cdetect = 1;
+ break;
+
+ }
+
+ return cdetect;
+}
+
+
+/****************************************************************/
+//reset the SDMMC controller of the current host
+/****************************************************************/
+int rk29_sdmmc_reset_controller(struct rk29_sdmmc *host)
+{
+ u32 value = 0;
+ int timeOut = 0;
+
+ rk29_sdmmc_write(host->regs, SDMMC_PWREN, POWER_ENABLE);
+
+ /* reset SDMMC IP */
+ //SDPAM_SDCClkEnable(host, TRUE);
+
+ //Clean the fifo.
+ for(timeOut=0; timeOut<FIFO_DEPTH; timeOut++)
+ {
+ if(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_EMPTY)
+ break;
+
+ value = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+ }
+
+ /* reset */
+#if defined(CONFIG_ARCH_RK29)
+ rk29_sdmmc_write(host->regs, SDMMC_CTRL,(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET ));
+#elif defined(CONFIG_ARCH_RK30)
+ rk29_sdmmc_write(host->regs, SDMMC_CTRL,(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+#endif
+ timeOut = 1000;
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+ while (( value & (SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_RESET)) && (timeOut > 0))
+ {
+ udelay(1);
+ timeOut--;
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+ }
+
+ if (timeOut == 0)
+ {
+ printk("%s..%s..%d.. reset controller fail!!! =====xbw[%s]=====\n",\
+ __FILE__, __FUNCTION__,__LINE__, host->dma_name);
+
+ host->errorstep = 0x0A;
+ return SDM_WAIT_FOR_FIFORESET_TIMEOUT;
+ }
+
+ /* FIFO threshold settings */
+ rk29_sdmmc_write(host->regs, SDMMC_FIFOTH, (SD_MSIZE_16 | (RX_WMARK << RX_WMARK_SHIFT) | (TX_WMARK << TX_WMARK_SHIFT)));
+
+ rk29_sdmmc_write(host->regs, SDMMC_CTYPE, SDMMC_CTYPE_1BIT);
+ rk29_sdmmc_write(host->regs, SDMMC_CLKSRC, CLK_DIV_SRC_0);
+ /* config debounce */
+ host->bus_hz = clk_get_rate(host->clk);
+ if((host->bus_hz > 52000000) || (host->bus_hz <= 0))
+ {
+ printk("%s..%s..%d..****Error!!!!!! Bus clock %d hz is beyond the prescribed limits ====xbw[%s]===\n",\
+ __FILE__, __FUNCTION__,__LINE__,host->bus_hz, host->dma_name);
+
+ host->errorstep = 0x0B;
+ return SDM_PARAM_ERROR;
+ }
+
+ rk29_sdmmc_write(host->regs, SDMMC_DEBNCE, (DEBOUNCE_TIME*host->bus_hz)&0xFFFFFF);
+
+ /* config interrupt */
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
+
+ if(host->use_dma)
+ {
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA);
+ }
+ else
+ {
+ if(0== host->pdev->id)
+ {
+ #if !defined(CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD)
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA | SDMMC_INT_SDIO);
+ #else
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA);
+ #endif
+ }
+ else if(1== host->pdev->id)
+ {
+ #if !defined(CONFIG_USE_SDMMC1_FOR_WIFI_DEVELOP_BOARD)
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA | SDMMC_INT_SDIO);
+ #else
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA);
+ #endif
+ }
+ else
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA | SDMMC_INT_SDIO);
+ }
+ }
+ }
+ else
+ {
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEIO);
}
- //rk29_sdmmc_write(host->regs, SDMMC_INTMASK,0);
- div = (((host->bus_hz + (host->bus_hz / 5)) / host->clock)) >> 1;
- if(!div)
- div = 1;
- /* store the actual clock for calculations */
- host->clock = (host->bus_hz / div) >> 1;
- /* disable clock */
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA, 0);
- rk29_sdmmc_write(host->regs, SDMMC_CLKSRC,0);
- /* inform CIU */
- sdmmc_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
- /* set clock to desired speed */
- rk29_sdmmc_write(host->regs, SDMMC_CLKDIV, div);
- /* inform CIU */
- sdmmc_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
- /* enable clock */
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA, SDMMC_CLKEN_ENABLE);
- /* inform CIU */
- sdmmc_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
-
- host->current_speed = host->clock;
+ else
+ {
+ if(0== host->pdev->id)
+ {
+ #if !defined(CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD)
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEIO | SDMMC_INT_SDIO);
+ #else
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEIO);
+ #endif
+ }
+ else if(1== host->pdev->id)
+ {
+ #if !defined(CONFIG_USE_SDMMC1_FOR_WIFI_DEVELOP_BOARD)
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEIO | SDMMC_INT_SDIO);
+ #else
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEIO);
+ #endif
+ }
+ else
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_INTMASK,RK29_SDMMC_INTMASK_USEDMA | SDMMC_INT_SDIO);
+ }
+ }
+ }
+
+ rk29_sdmmc_write(host->regs, SDMMC_PWREN, POWER_ENABLE);
+
+ rk29_sdmmc_write(host->regs, SDMMC_CTRL,SDMMC_CTRL_INT_ENABLE); // enable mci interrupt
+
+ return SDM_SUCCESS;
+}
+
+
+
+
+//enable/disnable the clk.
+static int rk29_sdmmc_control_clock(struct rk29_sdmmc *host, bool enable)
+{
+ u32 value = 0;
+ int tmo = 0;
+ int ret = SDM_SUCCESS;
+
+ //wait previous start to clear
+ tmo = 1000;
+ while (--tmo && (rk29_sdmmc_read(host->regs, SDMMC_CMD) & SDMMC_CMD_START))
+ {
+ udelay(1);//cpu_relax();
+ }
+ if(!tmo)
+ {
+ host->errorstep = 0x0C;
+ ret = SDM_START_CMD_FAIL;
+ goto Error_exit;
+ }
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ //SDMMC use low-power mode
+ #if SDMMC_CLOCK_TEST
+ if (enable)
+ {
+ value = (SDMMC_CLKEN_ENABLE);
+ }
+ else
+ {
+ value = (SDMMC_CLKEN_DISABLE);
+ }
+
+ #else
+ {
+ if (enable)
+ {
+ value = (SDMMC_CLKEN_LOW_PWR | SDMMC_CLKEN_ENABLE);
+ }
+ else
+ {
+ value = (SDMMC_CLKEN_LOW_PWR | SDMMC_CLKEN_DISABLE);
+ }
+ }
+ #endif
+ }
+ else
+ {
+ //SDIO-card use non-low-power mode
+ if (enable)
+ {
+ value = (SDMMC_CLKEN_ENABLE);
+ }
+ else
+ {
+ value = (SDMMC_CLKEN_DISABLE);
+ }
+ }
+
+ rk29_sdmmc_write(host->regs, SDMMC_CLKENA, value);
+
+ /* inform CIU */
+ ret = sdmmc_send_cmd_start(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT);
+ if(ret != SDM_SUCCESS)
+ {
+ goto Error_exit;
+ }
+
+ return SDM_SUCCESS;
+
+Error_exit:
+ printk("\n%s....%d.. control clock fail!!! Enable=%d, ret=0x%x ===xbw[%s]====\n",\
+ __FILE__,__LINE__,enable,ret, host->dma_name);
+
+ return ret;
+
+}
+
+
+//adjust the frequency.ie, to set the frequency division control
+int rk29_sdmmc_change_clk_div(struct rk29_sdmmc *host, u32 freqHz)
+{
+ u32 div;
+ u32 tmo;
+ int ret = SDM_SUCCESS;
+
+ if(0 == freqHz)
+ {
+ ret = SDM_PARAM_ERROR;
+ goto SetFreq_error;
+ }
+
+ ret = rk29_sdmmc_control_clock(host, FALSE);
+ if (ret != SDM_SUCCESS)
+ {
+ goto SetFreq_error;
+ }
+
+
+ host->bus_hz = clk_get_rate(host->clk);
+ if((host->bus_hz > 52000000) || (host->bus_hz <= 0))
+ {
+ printk("%s..%s..%d..****Error!!!!!! Bus clock %d hz is beyond the prescribed limits ====xbw[%s]===\n",\
+ __FILE__, __FUNCTION__,__LINE__,host->bus_hz, host->dma_name);
+
+ host->errorstep = 0x0D;
+ ret = SDM_PARAM_ERROR;
+ goto SetFreq_error;
+ }
+
+ //calculate the divider
+ div = host->bus_hz/freqHz + ((( host->bus_hz%freqHz ) > 0) ? 1:0 );
+ if( (div & 0x01) && (1 != div) )
+ {
+ //It is sure that the value of div is even.
+ ++div;
+ }
+
+ if(div > 1)
+ {
+ host->clock = host->bus_hz/div;
+ }
+ else
+ {
+ host->clock = host->bus_hz;
+ }
+ div = (div >> 1);
+
+ //wait previous start to clear
+ tmo = 1000;
+ while (--tmo && (rk29_sdmmc_read(host->regs, SDMMC_CMD) & SDMMC_CMD_START))
+ {
+ udelay(1);//cpu_relax();
}
+ if(!tmo)
+ {
+ host->errorstep = 0x0E;
+ ret = SDM_START_CMD_FAIL;
+ goto SetFreq_error;
+ }
+
+ /* set clock to desired speed */
+ rk29_sdmmc_write(host->regs, SDMMC_CLKDIV, div);
+
+ /* inform CIU */
+ ret = sdmmc_send_cmd_start(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT);
+ if(ret != SDM_SUCCESS)
+ {
+ host->errorstep = 0x0E1;
+ goto SetFreq_error;
+ }
+
+ if(host->old_div != div)
+ {
+ printk("%s..%d.. newDiv=%u, newCLK=%uKhz====xbw[%s]=====\n", \
+ __FUNCTION__, __LINE__,div, host->clock/1000, host->dma_name);
+ }
+
+ ret = rk29_sdmmc_control_clock(host, TRUE);
+ if(ret != SDM_SUCCESS)
+ {
+ goto SetFreq_error;
+ }
+ host->old_div = div;
- /* Set the current bus width */
+ return SDM_SUCCESS;
+
+SetFreq_error:
+
+ printk("%s..%d.. change division fail, errorStep=0x%x,ret=%d !!! ====xbw[%s]====\n",\
+ __FILE__, __LINE__,host->errorstep,ret, host->dma_name);
+
+ return ret;
+
+}
+
+int rk29_sdmmc_hw_init(void *data)
+{
+ struct rk29_sdmmc *host = (struct rk29_sdmmc *)data;
+
+ //set the iomux
+ host->ctype = SDMMC_CTYPE_1BIT;
+ host->set_iomux(host->pdev->id, host->ctype);
+
+ /* reset controller */
+ rk29_sdmmc_reset_controller(host);
+
+ rk29_sdmmc_change_clk_div(host, FOD_FREQ);
+
+ return SDM_SUCCESS;
+}
+
+
+
+int rk29_sdmmc_set_buswidth(struct rk29_sdmmc *host)
+{
+ //int ret;
+ switch (host->ctype)
+ {
+ case SDMMC_CTYPE_1BIT:
+ case SDMMC_CTYPE_4BIT:
+ break;
+ case SDMMC_CTYPE_8BIT:
+ return SDM_PARAM_ERROR; //Now, not support 8 bit width
+ default:
+ return SDM_PARAM_ERROR;
+ }
+
+ host->set_iomux(host->pdev->id, host->ctype);
+
+ /* Set the current bus width */
rk29_sdmmc_write(host->regs, SDMMC_CTYPE, host->ctype);
+
+ return SDM_SUCCESS;
+}
+
+
+static void rk29_sdmmc_dealwith_timeout(struct rk29_sdmmc *host)
+{
+ if(0 == host->mmc->doneflag)
+ return; //not to generate error flag if the command has been over.
+
+ switch(host->state)
+ {
+ case STATE_IDLE:
+ {
+ #if 1
+ break;
+ #else
+ if(!host->cmd)
+ break;
+
+ host->cmd->error = -EIO;
+
+ if(host->cmd->data)
+ {
+ host->cmd->data->error = -EILSEQ;
+ }
+ host->state = STATE_SENDING_CMD;
+ /* fall through */
+ #endif
+ }
+
+ case STATE_SENDING_CMD:
+ host->cmd_status |= SDMMC_INT_RTO;
+ host->cmd->error = -ETIME;
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,(SDMMC_INT_CMD_DONE | SDMMC_INT_RTO)); // clear interrupt
+ rk29_sdmmc_set_pending(host, EVENT_CMD_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+ break;
+ case STATE_DATA_BUSY:
+ host->data_status |= (SDMMC_INT_DCRC|SDMMC_INT_EBE);
+ host->cmd->data->error = -EILSEQ;
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_DTO); // clear interrupt
+ rk29_sdmmc_set_pending(host, EVENT_DATA_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+ break;
+ case STATE_SENDING_STOP:
+ host->cmd_status |= SDMMC_INT_RTO;
+ host->cmd->error = -ETIME;
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,(SDMMC_INT_CMD_DONE | SDMMC_INT_RTO)); // clear interrupt
+ rk29_sdmmc_set_pending(host, EVENT_CMD_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+ break;
+ }
}
-static void rk29_sdmmc_start_request(struct rk29_sdmmc *host)
+
+static void rk29_sdmmc_INT_CMD_DONE_timeout(unsigned long host_data)
{
+ struct rk29_sdmmc *host = (struct rk29_sdmmc *) host_data;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ if(STATE_SENDING_CMD == host->state)
+ {
+ if(0==host->cmd->retries)
+ {
+ printk("%s..%d... cmd=%d, INT_CMD_DONE timeout, errorStep=0x%x, host->state=%x ===xbw[%s]===\n",\
+ __FUNCTION__, __LINE__,host->cmd->opcode, host->errorstep,host->state,host->dma_name);
+ }
+
+ rk29_sdmmc_dealwith_timeout(host);
+ }
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+}
+
+
+static void rk29_sdmmc_INT_DTO_timeout(unsigned long host_data)
+{
+ struct rk29_sdmmc *host = (struct rk29_sdmmc *) host_data;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+
+ if( (host->cmdr & SDMMC_CMD_DAT_EXP) && (STATE_DATA_BUSY == host->state))
+ {
+ if(0==host->cmd->retries)
+ {
+ printk("%s..%d...cmd=%d DTO_timeout,cmdr=0x%x, errorStep=0x%x, Hoststate=%x===xbw[%s]===\n", \
+ __FUNCTION__, __LINE__,host->cmd->opcode,host->cmdr ,host->errorstep,host->state,host->dma_name);
+ }
+
+ rk29_sdmmc_dealwith_timeout(host);
+ }
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+
+}
+
+
+//to excute a request
+static int rk29_sdmmc_start_request(struct mmc_host *mmc )
+{
+ struct rk29_sdmmc *host = mmc_priv(mmc);
struct mmc_request *mrq;
struct mmc_command *cmd;
- struct mmc_data *data;
- u32 cmdflags;
+ u32 cmdr, ret;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ mrq = host->new_mrq;
+ cmd = mrq->cmd;
+ cmd->error = 0;
+
+ cmdr = rk29_sdmmc_prepare_command(cmd);
+ ret = SDM_SUCCESS;
+
+
+ /*clean FIFO if it is a new request*/
+ if((RK29_CTRL_SDMMC_ID == host->pdev->id) && ( !(cmdr & SDMMC_CMD_STOP)))
+ {
+ ret = rk29_sdmmc_reset_fifo(host);
+ if(SDM_SUCCESS != ret)
+ {
+ host->mrq = host->new_mrq;///
+ cmd->error = -ENOMEDIUM;
+ host->errorstep = 0x0F;
+ ret = SDM_FALSE;
+ goto start_request_Err;
+ }
+ }
+
+ //check data-busy if the current command has the bit13 in command register.
+ if( cmdr & SDMMC_CMD_PRV_DAT_WAIT )
+ {
+ if(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_DATA_BUSY)
+ {
+ host->mrq = host->new_mrq;///
+ cmd->error = -ETIMEDOUT;
+ ret = SDM_BUSY_TIMEOUT;
+ host->errorstep = 0x10;
+ if(0 == cmd->retries)
+ {
+ printk("%s..Error happen in CMD_PRV_DAT_WAIT. STATUS-reg=0x%x ===xbw[%s]===\n", \
+ __FUNCTION__, rk29_sdmmc_read(host->regs, SDMMC_STATUS),host->dma_name);
+ }
+ rk29_sdmmc_clear_fifo(host);
+ goto start_request_Err;
+ }
+ }
+
+ host->state = STATE_SENDING_CMD;
+ host->mrq = host->new_mrq;
mrq = host->mrq;
+ cmd = mrq->cmd;
+ cmd->error = 0;
+ cmd->data = NULL;
+
+ host->cmdr = cmdr;
+ host->cmd = cmd;
+ host->data_status = 0;
+ host->data = NULL;
+
+ host->errorstep = 0;
+ host->dodma = 0;
+
- rk29_sdmmc_wait_unbusy(host);
- if(!(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_EMPTY)) {
- rk29_sdmmc_reset_fifo(host);
+ //setting for the data
+ rk29_sdmmc_submit_data(host, mrq->data);
+ host->errorstep = 0xff;
+
+ xbwprintk(7, "%s..%d... CMD%d begin to call rk29_sdmmc_start_command() ===xbw[%s]===\n", \
+ __FUNCTION__, __LINE__ , cmd->opcode,host->dma_name);
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ mod_timer(&host->request_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_SEND_START_TIMEOUT+700));
}
- /* Slot specific timing and width adjustment */
- rk29_sdmmc_setup_bus(host);
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
- if(rk29_sdmmc_read(host->regs, SDMMC_INTMASK) & SDMMC_INT_SDIO)
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_SDIO |SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD);
else
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD);
- host->curr_mrq = mrq;
- host->pending_events = 0;
- host->completed_events = 0;
- host->data_status = 0;
- data = mrq->data;
- if (data) {
- rk29_sdmmc_set_timeout(host,data);
- rk29_sdmmc_write(host->regs, SDMMC_BYTCNT,data->blksz*data->blocks);
- rk29_sdmmc_write(host->regs, SDMMC_BLKSIZ,data->blksz);
+ {
+ mod_timer(&host->request_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_SEND_START_TIMEOUT+500));
}
- cmd = mrq->cmd;
- cmdflags = rk29_sdmmc_prepare_command(host->mmc, cmd);
- if (unlikely(test_and_clear_bit(RK29_SDMMC_CARD_NEED_INIT, &host->flags)))
- cmdflags |= SDMMC_CMD_INIT; //this is the first command, let set send the initializtion clock
-
- if (data) //we may need to move this code to mci_start_command
- rk29_sdmmc_submit_data(host, data);
+
+
+ ret = rk29_sdmmc_start_command(host, cmd, host->cmdr);
+ if(SDM_SUCCESS != ret)
+ {
+ cmd->error = -ETIMEDOUT;
+ if(0==cmd->retries)
+ {
+ printk("%s..%d... start_command(CMD%d, arg=%x, retries=%d) fail! ret=%d =========xbw=[%s]===\n",\
+ __FUNCTION__, __LINE__ , cmd->opcode,cmd->arg, cmd->retries,ret, host->dma_name);
+ }
+ host->errorstep = 0x11;
+ del_timer_sync(&host->request_timer);
+
+ goto start_request_Err;
+ }
+ host->errorstep = 0xfd;
+
+ xbwprintk(7, "%s..%d... CMD=%d, wait for INT_CMD_DONE, ret=%d , \n \
+ host->state=0x%x, cmdINT=0x%x \n host->pendingEvent=0x%lu, host->completeEvents=0x%lu =========xbw=[%s]=====\n\n",\
+ __FUNCTION__, __LINE__, host->cmd->opcode,ret, \
+ host->state,host->cmd_status, host->pending_events,host->completed_events,host->dma_name);
+
+ spin_unlock_irqrestore(&host->lock, iflags);
+ return SDM_SUCCESS;
- rk29_sdmmc_start_command(host, cmd, cmdflags);
+start_request_Err:
+ rk29_sdmmc_start_error(host);
- if (mrq->stop)
- host->stop_cmdr = rk29_sdmmc_prepare_command(host->mmc, mrq->stop);
+ if(0 == cmd->retries)
+ {
+ printk("%s: CMD%d(arg=%x) fail to start request. err=%d, Errorstep=0x%x ===xbw[%s]==\n\n",\
+ __FUNCTION__, cmd->opcode, cmd->arg,ret,host->errorstep,host->dma_name);
+ }
+
+ host->state = STATE_IDLE; //modifyed by xbw at 2011-08-15
+
+ if(host->mrq && host->mmc->doneflag)
+ {
+ host->mmc->doneflag = 0;
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ mmc_request_done(host->mmc, host->mrq);
+ }
+ else
+ {
+ spin_unlock_irqrestore(&host->lock, iflags);
+ }
+
+ return ret;
}
-static void rk29_sdmmc_queue_request(struct rk29_sdmmc *host,struct mmc_request *mrq)
-{
- spin_lock(&host->lock);
- host->mrq = mrq;
- if (host->state == STATE_IDLE) {
- host->state = STATE_SENDING_CMD;
- rk29_sdmmc_start_request(host);
- } else {
- list_add_tail(&host->queue_node, &host->queue);
+
+static void rk29_sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ unsigned long iflags;
+ struct rk29_sdmmc *host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ #if 0
+ //set 1 to close the controller for Debug.
+ if(RK29_CTRL_SDIO1_ID==host->pdev->id)//if(RK29_CTRL_SDMMC_ID==host->pdev->id)//
+ {
+ mrq->cmd->error = -ENOMEDIUM;
+ printk("%s..%d.. ==== The %s had been closed by myself for the experiment. ====xbw[%s]===\n",\
+ __FUNCTION__, __LINE__, host->dma_name, host->dma_name);
+
+ host->state = STATE_IDLE;
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
+ spin_unlock_irqrestore(&host->lock, iflags);
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+ #endif
+
+ xbwprintk(6, "\n%s..%d..New cmd=%2d(arg=0x%8x)=== cardPresent=0x%lu, state=0x%x ==xbw[%s]==\n", \
+ __FUNCTION__, __LINE__,mrq->cmd->opcode, mrq->cmd->arg,host->flags,host->state, host->dma_name);
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ if(!rk29_sdmmc_get_cd(mmc) || ((0==mmc->re_initialized_flags)&&(MMC_GO_IDLE_STATE != mrq->cmd->opcode)))
+ {
+ mrq->cmd->error = -ENOMEDIUM;
+
+ if((RK29_CTRL_SDMMC_ID == host->pdev->id)&&(0==mrq->cmd->retries))
+ {
+ if(host->old_cmd != mrq->cmd->opcode)
+ {
+ if( ((17==host->old_cmd)&&(18==mrq->cmd->opcode)) || ((18==host->old_cmd)&&(17==mrq->cmd->opcode)) ||\
+ ((24==host->old_cmd)&&(25==mrq->cmd->opcode)) || ((25==host->old_cmd)&&(24==mrq->cmd->opcode)))
+ {
+ host->old_cmd = mrq->cmd->opcode;
+ if(host->error_times++ %RK29_ERROR_PRINTK_INTERVAL ==0)
+ {
+ printk("%s: Refuse to run CMD%2d(arg=0x%8x) due to the removal of card. 1==xbw[%s]==\n", \
+ __FUNCTION__, mrq->cmd->opcode, mrq->cmd->arg, host->dma_name);
+ }
+ }
+ else
+ {
+ host->old_cmd = mrq->cmd->opcode;
+ host->error_times = 0;
+ printk("%s: Refuse to run CMD%2d(arg=0x%8x) due to the removal of card. 2==xbw[%s]==\n", \
+ __FUNCTION__, mrq->cmd->opcode, mrq->cmd->arg, host->dma_name);
+ }
+ }
+ else
+ {
+ if(host->error_times++ % (RK29_ERROR_PRINTK_INTERVAL*3) ==0)
+ {
+ printk("%s: Refuse to run CMD%2d(arg=0x%8x) due to the removal of card. 3==xbw[%s]==\n", \
+ __FUNCTION__, mrq->cmd->opcode, mrq->cmd->arg, host->dma_name);
+ }
+ host->old_cmd = mrq->cmd->opcode;
+ }
+ }
+ host->state = STATE_IDLE;
+ spin_unlock_irqrestore(&host->lock, iflags);
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+ else
+ {
+ if(host->old_cmd != mrq->cmd->opcode)
+ {
+ host->old_cmd = mrq->cmd->opcode;
+ host->error_times = 0;
+ }
+ }
+ }
+ else
+ {
+ host->old_cmd = mrq->cmd->opcode;
+ host->error_times = 0;
+
+ if(!test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags))
+ {
+ host->state = STATE_IDLE;
+ mrq->cmd->error = -ENOMEDIUM;
+ spin_unlock_irqrestore(&host->lock, iflags);
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
}
- spin_unlock(&host->lock);
-}
-
-static void rk29_sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
-{
- struct rk29_sdmmc *host = mmc_priv(mmc);
+
+ #if 1
+ host->new_mrq = mrq;
- WARN_ON(host->mrq);
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ rk29_sdmmc_start_request(mmc);
- if (!test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags)) {
+ #else
+ if (host->state == STATE_IDLE)
+ {
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ host->new_mrq = mrq;
+ rk29_sdmmc_start_request(mmc);
+ }
+ else
+ {
+ #ifdef RK29_SDMMC_LIST_QUEUE
+
+ printk("%s..%d...Danger! Danger! New request was added to queue. ===xbw[%s]===\n", \
+ __FUNCTION__, __LINE__,host->dma_name);
+ list_add_tail(&host->queue_node, &host->queue);
+
+ #else
+
+ printk("%s..%d..state Error! ,old_state=%d, OldCMD=%d ,NewCMD%2d,arg=0x%x ===xbw[%s]===\n", \
+ __FUNCTION__, __LINE__, host->state, host->cmd->opcode,mrq->cmd->opcode,mrq->cmd->arg, host->dma_name);
+
mrq->cmd->error = -ENOMEDIUM;
+
+ spin_unlock_irqrestore(&host->lock, iflags);
mmc_request_done(mmc, mrq);
+
return;
- }
- rk29_sdmmc_queue_request(host,mrq);
+
+ #endif
+ }
+ #endif
+
}
+
+
static void rk29_sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
- struct rk29_sdmmc *host = mmc_priv(mmc);;
+ int timeout = 250;
+ unsigned int value;
+ unsigned long iflags;
+ struct rk29_sdmmc *host = mmc_priv(mmc);
- host->ctype = 0; // set default 1 bit mode
+ spin_lock_irqsave(&host->lock, iflags);
+
+ if(test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags) || (RK29_CTRL_SDMMC_ID == host->pdev->id))
+ {
+ /*
+ * Waiting SDIO controller to be IDLE.
+ */
+ while (timeout-- > 0)
+ {
+ value = rk29_sdmmc_read(host->regs, SDMMC_STATUS);
+ if ((value & SDMMC_STAUTS_DATA_BUSY) == 0 &&(value & SDMMC_CMD_FSM_MASK) == SDMMC_CMD_FSM_IDLE)
+ {
+ break;
+ }
+
+ mdelay(1);
+ }
+ if (timeout <= 0)
+ {
+ printk("%s..%d...Waiting for SDMMC%d controller to be IDLE timeout.==xbw[%s]===\n", \
+ __FUNCTION__, __LINE__, host->pdev->id, host->dma_name);
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_1:
- host->ctype = 0;
- break;
- case MMC_BUS_WIDTH_4:
- host->ctype = SDMMC_CTYPE_4BIT;
- break;
+ goto out;
+ }
+ }
+
+ //if(host->bus_mode != ios->power_mode)
+ {
+ switch (ios->power_mode)
+ {
+ case MMC_POWER_UP:
+ rk29_sdmmc_write(host->regs, SDMMC_PWREN, POWER_ENABLE);
+
+ //reset the controller if it is SDMMC0
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ xbwprintk(7, "%s..%d..POWER_UP, call reset_controller, initialized_flags=%d ====xbw[%s]=====\n",\
+ __FUNCTION__, __LINE__, host->mmc->re_initialized_flags,host->dma_name);
+
+ mdelay(5);
+
+ rk29_sdmmc_hw_init(host);
+ }
+
+ break;
+ case MMC_POWER_OFF:
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ rk29_sdmmc_control_clock(host, FALSE);
+ rk29_sdmmc_write(host->regs, SDMMC_PWREN, POWER_DISABLE);
+
+ if(5 == host->bus_mode)
+ {
+ mdelay(5);
+ xbwprintk(7, "%s..%d..Fisrt powerOFF, call reset_controller ======xbw[%s]====\n", \
+ __FUNCTION__, __LINE__,host->dma_name);
+
+ rk29_sdmmc_reset_controller(host);
+ }
+
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ host->bus_mode = ios->power_mode;
+
+ }
+
+ if(!(test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags) || (RK29_CTRL_SDIO1_ID != host->pdev->id)))
+ goto out; //exit the set_ios directly if the SDIO is not present.
+
+ if(host->ctype != ios->bus_width)
+ {
+ switch (ios->bus_width)
+ {
+ case MMC_BUS_WIDTH_1:
+ host->ctype = SDMMC_CTYPE_1BIT;
+ break;
+ case MMC_BUS_WIDTH_4:
+ host->ctype = SDMMC_CTYPE_4BIT;
+ break;
+ case MMC_BUS_WIDTH_8:
+ host->ctype = SDMMC_CTYPE_8BIT;
+ break;
+ default:
+ host->ctype = 0;
+ break;
+ }
+
+ rk29_sdmmc_set_buswidth(host);
+
}
- if (ios->clock) {
- spin_lock(&host->lock);
+
+ if (ios->clock && (ios->clock != host->clock))
+ {
/*
* Use mirror of ios->clock to prevent race with mmc
* core ios update when finding the minimum.
*/
- host->clock = ios->clock;
-
- spin_unlock(&host->lock);
- } else {
- spin_lock(&host->lock);
- host->clock = 0;
- spin_unlock(&host->lock);
- }
- switch (ios->power_mode) {
- case MMC_POWER_UP:
- set_bit(RK29_SDMMC_CARD_NEED_INIT, &host->flags);
- rk29_sdmmc_write(host->regs, SDMMC_PWREN, 1);
- break;
- default:
- break;
+ //host->clock = ios->clock;
+ rk29_sdmmc_change_clk_div(host, ios->clock);
}
+out:
+
+ spin_unlock_irqrestore(&host->lock, iflags);
}
static int rk29_sdmmc_get_ro(struct mmc_host *mmc)
{
- struct rk29_sdmmc *host = mmc_priv(mmc);
- u32 wrtprt = rk29_sdmmc_read(host->regs, SDMMC_WRTPRT);
+ struct rk29_sdmmc *host = mmc_priv(mmc);
+ int ret=0;
- return (wrtprt & SDMMC_WRITE_PROTECT)?1:0;
-}
+ switch(host->pdev->id)
+ {
+ case 0:
+ {
+ #if defined(CONFIG_SDMMC0_RK29_WRITE_PROTECT) || defined(CONFIG_SDMMC1_RK29_WRITE_PROTECT)
+ if(INVALID_GPIO == host->write_protect)
+ ret = 0;//no write-protect
+ else
+ ret = gpio_get_value(host->write_protect)?1:0;
+
+ xbwprintk(7,"%s..%d.. write_prt_pin=%d, get_ro=%d ===xbw[%s]===\n",\
+ __FUNCTION__, __LINE__,host->write_protect, ret, host->dma_name);
+
+ #else
+ u32 wrtprt = rk29_sdmmc_read(host->regs, SDMMC_WRTPRT);
+
+ ret = (wrtprt & SDMMC_WRITE_PROTECT)?1:0;
+ #endif
+
+ break;
+ }
+
+ case 1:
+ ret = 0;//no write-protect
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
-static int rk29_sdmmc_get_cd(struct mmc_host *mmc)
-{
-#if defined(CONFIG_MACH_RK29_A22) //A22's sdcard do not support detect pin
-return 1;
-#else
- struct rk29_sdmmc *host = mmc_priv(mmc);
- u32 cdetect = rk29_sdmmc_read(host->regs, SDMMC_CDETECT);
- return (cdetect & SDMMC_CARD_DETECT_N)?0:1;
-#endif
}
+
static void rk29_sdmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
u32 intmask;
spin_lock_irqsave(&host->lock, flags);
intmask = rk29_sdmmc_read(host->regs, SDMMC_INTMASK);
+
if(enable)
rk29_sdmmc_write(host->regs, SDMMC_INTMASK, intmask | SDMMC_INT_SDIO);
else
card->quirks = MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
}
+
+static int rk29_sdmmc_clear_fifo(struct rk29_sdmmc *host)
+{
+ unsigned int timeout, value;
+ int ret = SDM_SUCCESS;
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
+ }
+
+ rk29_sdmmc_stop_dma(host);
+ rk29_sdmmc_control_host_dma(host, FALSE);
+ host->dodma = 0;
+
+ //Clean the fifo.
+ for(timeout=0; timeout<FIFO_DEPTH; timeout++)
+ {
+ if(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_EMPTY)
+ break;
+
+ value = rk29_sdmmc_read(host->regs, SDMMC_DATA);
+ }
+
+ /* reset */
+ timeout = 1000;
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+ value |= (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET);
+ rk29_sdmmc_write(host->regs, SDMMC_CTRL, value);
+
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+
+ while( (value & (SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_RESET | SDMMC_CTRL_DMA_RESET)) && (timeout > 0))
+ {
+ udelay(1);
+ timeout--;
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+ }
+
+ if (timeout == 0)
+ {
+ host->errorstep = 0x0A;
+ ret = SDM_WAIT_FOR_FIFORESET_TIMEOUT;
+ }
+
+ return ret;
+}
+
+
+
static const struct mmc_host_ops rk29_sdmmc_ops[] = {
{
.request = rk29_sdmmc_request,
{
.request = rk29_sdmmc_request,
.set_ios = rk29_sdmmc_set_ios,
+ .get_ro = rk29_sdmmc_get_ro,
+ .get_cd = rk29_sdmmc_get_cd,
.enable_sdio_irq = rk29_sdmmc_enable_sdio_irq,
- .init_card = rk29_sdmmc_init_card,
+ .init_card = rk29_sdmmc_init_card,
},
};
-static void rk29_sdmmc_request_end(struct rk29_sdmmc *host, struct mmc_request *mrq)
- __releases(&host->lock)
- __acquires(&host->lock)
+static void rk29_sdmmc_request_end(struct rk29_sdmmc *host, struct mmc_command *cmd)
{
- struct mmc_host *prev_mmc = host->mmc;
-
- //WARN_ON(host->cmd || host->data);
- host->curr_mrq = NULL;
- host->mrq = NULL;
+ u32 status = host->data_status;
+ int output=SDM_SUCCESS;
- rk29_sdmmc_wait_unbusy(host);
+ xbwprintk(7, "%s..%d... cmd=%d, host->state=0x%x,\n pendingEvent=0x%lu, completeEvents=0x%lu ====xbw=[%s]====\n\n",\
+ __FUNCTION__, __LINE__,cmd->opcode,host->state, host->pending_events,host->completed_events,host->dma_name);
+
+ del_timer_sync(&host->DTO_timer);
+
+ if(RK29_CTRL_SDMMC_ID == host->pdev->id)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF); //added by xbw at 2011-08-15
+ }
+
+ //stop DMA
+ if(host->dodma)
+ {
+ rk29_sdmmc_stop_dma(host);
+ rk29_sdmmc_control_host_dma(host, FALSE);
+ host->dodma = 0;
+ }
+
+ if(cmd->error)
+ {
+ goto exit;//It need not to wait-for-busy if the CMD-ERROR happen.
+ }
+ host->errorstep = 0xf7;
+ if(cmd->data)
+ {
+ if(host->cmdr & SDMMC_CMD_DAT_WRITE)
+ {
+ if(status & (SDMMC_INT_DCRC | SDMMC_INT_EBE))
+ {
+ cmd->data->error = -EILSEQ;
+ output = SDM_DATA_CRC_ERROR;
+ host->errorstep = 0x16;
+ }
+ else
+ {
+ output = rk29_sdmmc_wait_unbusy(host);
+ if(SDM_SUCCESS != output)
+ {
+ host->errorstep = 0x17;
+ cmd->data->error = -ETIMEDOUT;
+ }
+
+ host->data->bytes_xfered = host->data->blocks * host->data->blksz;
+ }
+ }
+ else
+ {
+ if( status & SDMMC_INT_SBE)
+ {
+ cmd->data->error = -EIO;
+ host->errorstep = 0x18;
+ output = SDM_START_BIT_ERROR;
+ }
+ else if((status & SDMMC_INT_EBE) && (cmd->opcode != 14)) //MMC4.0, BUSTEST_R, A host read the reserved bus testing data parttern from a card.
+ {
+ cmd->data->error = -EILSEQ;
+ host->errorstep = 0x19;
+ output = SDM_END_BIT_ERROR;
+ }
+ else if(status & SDMMC_INT_DRTO)
+ {
+ cmd->data->error = -ETIMEDOUT;
+ host->errorstep = 0x1A;
+ output = SDM_DATA_READ_TIMEOUT;
+ }
+ else if(status & SDMMC_INT_DCRC)
+ {
+ host->errorstep = 0x1B;
+ cmd->data->error = -EILSEQ;
+ output = SDM_DATA_CRC_ERROR;
+ }
+ else
+ {
+ output = rk29_sdmmc_read_remain_data(host, (host->data->blocks * host->data->blksz), host->pbuf);
+ if(SDM_SUCCESS == output)
+ {
+ host->data->bytes_xfered = host->data->blocks * host->data->blksz;
+ }
+ }
+ }
+ }
+
+ if(SDM_SUCCESS == output)
+ {
+ if ((mmc_resp_type(cmd) == MMC_RSP_R1B) || (MMC_STOP_TRANSMISSION == cmd->opcode))
+ {
+ output = rk29_sdmmc_wait_unbusy(host);
+ if((SDM_SUCCESS != output) && (!host->mrq->cmd->error))
+ {
+ printk("%s..%d... CMD12 wait busy timeout!!!!! errorStep=0x%x ====xbw=[%s]====\n", \
+ __FUNCTION__, __LINE__, host->errorstep, host->dma_name);
+ rk29_sdmmc_clear_fifo(host);
+ cmd->error = -ETIMEDOUT;
+ host->mrq->cmd->error = -ETIMEDOUT;
+ host->errorstep = 0x1C;
+ }
+ }
+ }
+ host->errorstep = 0xf6;
+
+ //trace error
+ if(cmd->data && cmd->data->error)
+ {
+ if( (!cmd->error) && (0==cmd->retries))
+ {
+ printk("%s..%d......CMD=%d error!!!(arg=0x%x,cmdretry=%d,blksize=%d, blocks=%d), \n \
+ statusReg=0x%x, ctrlReg=0x%x, nerrorTimes=%d, errorStep=0x%x ====xbw[%s]====\n",\
+ __FUNCTION__, __LINE__, cmd->opcode, cmd->arg, cmd->retries,cmd->data->blksz, cmd->data->blocks,
+ rk29_sdmmc_read(host->regs, SDMMC_STATUS),
+ rk29_sdmmc_read(host->regs, SDMMC_CTRL),
+ host->error_times,host->errorstep, host->dma_name);
+ }
+ cmd->error = -ENODATA;
+ }
+ host->errorstep = 0xf5;
+
+exit:
+
+#ifdef RK29_SDMMC_LIST_QUEUE
+ if (!list_empty(&host->queue))
+ {
+ printk("%s..%d.. Danger!Danger!. continue the next request in the queue. ====xbw[%s]====\n",\
+ __FUNCTION__, __LINE__, host->dma_name);
- if(!(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_EMPTY)) {
- rk29_sdmmc_reset_fifo(host);
- }
- if (!list_empty(&host->queue)) {
host = list_entry(host->queue.next,
struct rk29_sdmmc, queue_node);
list_del(&host->queue_node);
host->state = STATE_SENDING_CMD;
- rk29_sdmmc_start_request(host);
- } else {
+ rk29_sdmmc_start_request(host->mmc);
+ }
+ else
+ {
dev_vdbg(&host->pdev->dev, "list empty\n");
host->state = STATE_IDLE;
}
-
- spin_unlock(&host->lock);
- if(rk29_sdmmc_read(host->regs, SDMMC_INTMASK) & SDMMC_INT_SDIO)
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_CD|SDMMC_INT_SDIO);
- else
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_CD);
- if(mrq && mrq->data && mrq->data->error) {
- //mrq->data->bytes_xfered = 0;
- rk29_sdmmc_write(host->regs, SDMMC_CMD, host->stop_cmdr | SDMMC_CMD_START);
- dev_info(&host->pdev->dev, "data error, request done!\n");
- }
- mmc_request_done(prev_mmc, mrq);
-
- spin_lock(&host->lock);
+#else
+ dev_vdbg(&host->pdev->dev, "list empty\n");
+ host->state = STATE_IDLE;
+#endif
+
}
-static void rk29_sdmmc_command_complete(struct rk29_sdmmc *host,
+static int rk29_sdmmc_command_complete(struct rk29_sdmmc *host,
struct mmc_command *cmd)
{
- u32 status = host->cmd_status;
+ u32 value, status = host->cmd_status;
+ int timeout, output= SDM_SUCCESS;
- host->cmd_status = 0;
+ xbwprintk(7, "%s..%d. cmd=%d, host->state=0x%x, cmdINT=0x%x\n,pendingEvent=0x%lu,completeEvents=0x%lu ===xbw[%s]===\n\n",\
+ __FUNCTION__, __LINE__,cmd->opcode,host->state,status, host->pending_events,host->completed_events,host->dma_name);
- if(cmd->flags & MMC_RSP_PRESENT) {
- if(cmd->flags & MMC_RSP_136) {
+ del_timer_sync(&host->request_timer);
+
+ host->cmd_status = 0;
- /* Read the response from the card (up to 16 bytes).
- * RK29 SDMMC controller saves bits 127-96 in SDMMC_RESP3
- * for easy parsing. But the UNSTUFF_BITS macro in core/mmc.c
- * core/sd.c expect those bits be in resp[0]. Hence
- * reverse the response word order.
- */
- cmd->resp[3] = rk29_sdmmc_read(host->regs, SDMMC_RESP0);
- cmd->resp[2] = rk29_sdmmc_read(host->regs, SDMMC_RESP1);
- cmd->resp[1] = rk29_sdmmc_read(host->regs, SDMMC_RESP2);
- cmd->resp[0] = rk29_sdmmc_read(host->regs, SDMMC_RESP3);
- } else {
+ if((RK29_CTRL_SDMMC_ID == host->pdev->id) && (host->cmdr & SDMMC_CMD_STOP))
+ {
+ output = rk29_sdmmc_reset_fifo(host);
+ if (SDM_SUCCESS != output)
+ {
+ printk("%s..%d......reset fifo fail! CMD%d(arg=0x%x, Retries=%d) =======xbw[%s]=====\n",__FUNCTION__, __LINE__, \
+ cmd->opcode, cmd->arg, cmd->retries,host->dma_name);
+
+ cmd->error = -ETIMEDOUT;
+ host->mrq->cmd->error = cmd->error;
+ output = SDM_ERROR;
+ host->errorstep = 0x1C;
+ goto CMD_Errror;
+ }
+ }
+
+ if(status & SDMMC_INT_RTO)
+ {
+ cmd->error = -ENOMEM;
+ host->mrq->cmd->error = cmd->error;
+ output = SDM_BUSY_TIMEOUT;
+
+ //rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_RTO);
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,0xFFFFFFFF); //modifyed by xbw at 2011-08-15
+
+ if(host->use_dma)//if(host->dodma)
+ {
+ if(host->dodma)
+ {
+ rk29_sdmmc_stop_dma(host);
+ rk29_sdmmc_control_host_dma(host, FALSE);
+ host->dodma = 0;
+ }
+
+ value = rk29_sdmmc_read(host->regs, SDMMC_CTRL);
+ value |= SDMMC_CTRL_FIFO_RESET;
+ rk29_sdmmc_write(host->regs, SDMMC_CTRL, value);
+
+ timeout = 1000;
+ while (((value = rk29_sdmmc_read(host->regs, SDMMC_CTRL)) & (SDMMC_CTRL_FIFO_RESET)) && (timeout > 0))
+ {
+ udelay(1);
+ timeout--;
+ }
+ if (timeout == 0)
+ {
+ output = SDM_FALSE;
+ host->errorstep = 0x1D;
+ printk("%s..%d......reset CTRL fail! CMD%d(arg=0x%x, Retries=%d) ===xbw[%s]===\n",\
+ __FUNCTION__, __LINE__, cmd->opcode, cmd->arg, cmd->retries,host->dma_name);
+
+ goto CMD_Errror;
+ }
+ }
+
+ }
+
+ if(cmd->flags & MMC_RSP_PRESENT)
+ {
+ if(cmd->flags & MMC_RSP_136)
+ {
+ cmd->resp[3] = rk29_sdmmc_read(host->regs, SDMMC_RESP0);
+ cmd->resp[2] = rk29_sdmmc_read(host->regs, SDMMC_RESP1);
+ cmd->resp[1] = rk29_sdmmc_read(host->regs, SDMMC_RESP2);
+ cmd->resp[0] = rk29_sdmmc_read(host->regs, SDMMC_RESP3);
+ }
+ else
+ {
cmd->resp[0] = rk29_sdmmc_read(host->regs, SDMMC_RESP0);
- cmd->resp[1] = 0;
- cmd->resp[2] = 0;
- cmd->resp[3] = 0;
}
}
+
+ if(cmd->error)
+ {
+ del_timer_sync(&host->DTO_timer);
+
+ //trace error
+ if((0==cmd->retries) && (host->error_times++%RK29_ERROR_PRINTK_INTERVAL == 0) && (12 != cmd->opcode))
+ {
+ if( ((RK29_CTRL_SDMMC_ID==host->pdev->id)&&(MMC_SLEEP_AWAKE!=cmd->opcode)) ||
+ ((RK29_CTRL_SDMMC_ID!=host->pdev->id)&&(MMC_SEND_EXT_CSD!=cmd->opcode)) )
+ {
+ printk("%s..%d...CMD%d(arg=0x%x), hoststate=%d, errorTimes=%d, errorStep=0x%x ! ===xbw[%s]===\n",\
+ __FUNCTION__, __LINE__, cmd->opcode, cmd->arg, host->state,host->error_times,host->errorstep, host->dma_name);
+ }
+ }
+
+ }
+ del_timer_sync(&host->request_timer);
+
+
+ return SDM_SUCCESS;
+
+CMD_Errror:
+ del_timer_sync(&host->request_timer);
+ del_timer_sync(&host->DTO_timer);
+
+ if((0==cmd->retries) && (host->error_times++%RK29_ERROR_PRINTK_INTERVAL == 0))
+ {
+ printk("%s..%d....command_complete(CMD=%d, arg=%x) error=%d =======xbw[%s]=====\n",\
+ __FUNCTION__, __LINE__, host->cmd->opcode,host->cmd->arg, output, host->dma_name);
+ }
+
+ return output;
+
+}
+
+
+static void rk29_sdmmc_start_error(struct rk29_sdmmc *host)
+{
+ host->cmd->error = -EIO;
+ host->mrq->cmd->error = -EIO;
+ host->cmd_status |= SDMMC_INT_RTO;
+
+ del_timer_sync(&host->request_timer);
+
+ rk29_sdmmc_command_complete(host, host->mrq->cmd);
+ rk29_sdmmc_request_end(host, host->mrq->cmd);
- if (status & SDMMC_INT_RTO)
- cmd->error = -ETIMEDOUT;
- else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
- cmd->error = -EILSEQ;
- else if (status & SDMMC_INT_RE)
- cmd->error = -EIO;
- else if(status & SDMMC_INT_HLE)
- cmd->error = -EIO;
- else
- cmd->error = 0;
-
- if (cmd->error) {
- dev_vdbg(&host->pdev->dev,
- "command error: status=0x%08x resp=0x%08x\n"
- "cmd=0x%08x arg=0x%08x flg=0x%08x err=%d\n",
- status, cmd->resp[0],
- cmd->opcode, cmd->arg, cmd->flags, cmd->error);
-
- if (cmd->data) {
- host->data = NULL;
- rk29_sdmmc_stop_dma(host);
- }
- }
}
static void rk29_sdmmc_tasklet_func(unsigned long priv)
{
struct rk29_sdmmc *host = (struct rk29_sdmmc *)priv;
- struct mmc_request *mrq = host->curr_mrq;
- struct mmc_data *data = host->data;
- struct mmc_command *cmd = host->cmd;
+ struct mmc_data *data = host->cmd->data;
enum rk29_sdmmc_state state = host->state;
- enum rk29_sdmmc_state prev_state;
- u32 status;
+ int pending_flag, stopflag;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ state = host->state;
+ pending_flag = 0;
+ stopflag = 0;
+
+ do
+ {
+ switch (state)
+ {
+ case STATE_IDLE:
+ {
+ xbwprintk(7, "%s..%d.. prev_state= STATE_IDLE ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->dma_name);
+ break;
+ }
- spin_lock(&host->lock);
+ case STATE_SENDING_CMD:
+ {
+ xbwprintk(7, "%s..%d.. prev_state= STATE_SENDING_CMD, pendingEvernt=0x%lu ====xbw[%s]====\n",\
+ __FUNCTION__, __LINE__,host->completed_events, host->dma_name);
- state = host->state;
+ if (!rk29_sdmmc_test_and_clear_pending(host, EVENT_CMD_COMPLETE))
+ break;
+ host->errorstep = 0xfb;
- do {
- prev_state = state;
-
- switch (state) {
- case STATE_IDLE:
- break;
-
- case STATE_SENDING_CMD:
- if (!rk29_sdmmc_test_and_clear_pending(host,
- EVENT_CMD_COMPLETE))
- break;
-
- host->cmd = NULL;
- rk29_sdmmc_set_completed(host, EVENT_CMD_COMPLETE);
- if(mrq)
- {
- rk29_sdmmc_command_complete(host, mrq->cmd);
- if (!mrq->data || (cmd && cmd->error)) {
- rk29_sdmmc_request_end(host, host->curr_mrq);
- goto unlock;
- }
- }
- prev_state = state = STATE_SENDING_DATA;
- /* fall through */
-
- case STATE_SENDING_DATA:
- if (rk29_sdmmc_test_and_clear_pending(host,
- EVENT_DATA_ERROR)) {
- rk29_sdmmc_stop_dma(host);
- if (data && data->stop)
- send_stop_cmd(host, data);
- state = STATE_DATA_ERROR;
- break;
- }
-
- if (!rk29_sdmmc_test_and_clear_pending(host,
- EVENT_XFER_COMPLETE))
- break;
-
- rk29_sdmmc_set_completed(host, EVENT_XFER_COMPLETE);
- prev_state = state = STATE_DATA_BUSY;
- /* fall through */
-
- case STATE_DATA_BUSY:
- if (!rk29_sdmmc_test_and_clear_pending(host,
- EVENT_DATA_COMPLETE))
- break;
- host->data = NULL;
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (rk29_sdmmc_read(host->regs, SDMMC_CTRL))&(~SDMMC_CTRL_DMA_ENABLE));
-
- rk29_sdmmc_set_completed(host, EVENT_DATA_COMPLETE);
- status = host->data_status;
- if(data) {
- if (unlikely(status & RK29_SDMMC_DATA_ERROR_FLAGS)) {
- if (status & SDMMC_INT_DRTO) {
- dev_err(&host->pdev->dev,
- "data timeout error\n");
- data->error = -ETIMEDOUT;
- } else if (status & SDMMC_INT_DCRC) {
- dev_err(&host->pdev->dev,
- "data CRC error\n");
- data->error = -EILSEQ;
- } else {
- dev_err(&host->pdev->dev,
- "data FIFO error (status=%08x)\n",
- status);
- data->error = -EIO;
- }
- }else {
- data->bytes_xfered = data->blocks * data->blksz;
- data->error = 0;
- }
- }
- if (!data->stop && host->curr_mrq) {
- rk29_sdmmc_request_end(host, host->curr_mrq);
- goto unlock;
- }
-
- prev_state = state = STATE_SENDING_STOP;
- if (data && !data->error)
- send_stop_cmd(host, data);
- /* fall through */
-
- case STATE_SENDING_STOP:
- if (!rk29_sdmmc_test_and_clear_pending(host,
- EVENT_CMD_COMPLETE))
- break;
-
- host->cmd = NULL;
- if(mrq)
- rk29_sdmmc_command_complete(host, mrq->stop);
- if(host->curr_mrq)
- rk29_sdmmc_request_end(host, host->curr_mrq);
- goto unlock;
- case STATE_DATA_ERROR:
- if (!rk29_sdmmc_test_and_clear_pending(host,
- EVENT_XFER_COMPLETE))
- break;
-
- state = STATE_DATA_BUSY;
- break;
- }
- } while (state != prev_state);
+ del_timer_sync(&host->request_timer); //delete the timer for INT_COME_DONE
- host->state = state;
+ rk29_sdmmc_set_completed(host, EVENT_CMD_COMPLETE);
+ rk29_sdmmc_command_complete(host, host->cmd);
-unlock:
- spin_unlock(&host->lock);
+
+ if (!data)
+ {
+ rk29_sdmmc_request_end(host, host->cmd);
-}
+ xbwprintk(7, "%s..%d.. CMD%d call mmc_request_done()====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__,host->cmd->opcode,host->dma_name);
+
+ host->complete_done = 1;
+ break;
+ }
+ host->errorstep = 0xfa;
+ if(host->cmd->error)
+ {
+ del_timer_sync(&host->DTO_timer); //delete the timer for INT_DTO
+
+ if(data->stop)
+ {
+ xbwprintk(7, "%s..%d.. cmderr, so call send_stop_cmd() ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->dma_name);
+
+ #if 0
+ state = STATE_SENDING_CMD;//STATE_SENDING_STOP;
+ send_stop_cmd(host);
+ #else
+ stopflag = 1; //Moidfyed by xbw at 2011-09-08
+ #endif
+ break;
+ }
+
+ rk29_sdmmc_set_pending(host, EVENT_DATA_COMPLETE);
+ }
+ host->errorstep = 0xf9;
+ state = STATE_DATA_BUSY;
+ /* fall through */
+ }
+ case STATE_DATA_BUSY:
+ {
+ xbwprintk(7, "%s..%d.. prev_state= STATE_DATA_BUSY, pendingEvernt=0x%lu ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__,host->pending_events, host->dma_name);
-inline static void rk29_sdmmc_push_data(struct rk29_sdmmc *host, void *buf,int cnt)
-{
- u32* pData = (u32*)buf;
+ if (!rk29_sdmmc_test_and_clear_pending(host, EVENT_DATA_COMPLETE))
+ break;
+ host->errorstep = 0xf8;
+ rk29_sdmmc_set_completed(host, EVENT_DATA_COMPLETE);
+ del_timer_sync(&host->DTO_timer); //delete the timer for INT_DTO
- if (cnt % 4 != 0)
- cnt = (cnt>>2) +1;
- else
- cnt = cnt >> 2;
- while (cnt > 0) {
- rk29_sdmmc_write(host->regs, SDMMC_DATA,*pData++);
- cnt--;
+ rk29_sdmmc_request_end(host, host->cmd);
+
+ if (data && !data->stop)
+ {
+ xbwprintk(7, "%s..%d.. CMD%d call mmc_request_done()====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__,host->cmd->opcode,host->dma_name);
+
+ if(!( (MMC_READ_SINGLE_BLOCK == host->cmd->opcode)&&( -EIO == data->error))) //deal with START_BIT_ERROR
+ {
+ host->complete_done = 2;
+ break;
+ }
+
+ }
+ host->errorstep = 0xf4;
+ xbwprintk(7, "%s..%d.. after DATA_COMPLETE, so call send_stop_cmd() ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->dma_name);
+
+ #if 0
+ state = STATE_SENDING_CMD;
+ send_stop_cmd(host);
+ #else
+ stopflag = 2; //Moidfyed by xbw at 2011-09-08
+ #endif
+
+ break;
+ }
+
+ case STATE_SENDING_STOP:
+ {
+ xbwprintk(7, "%s..%d.. prev_state= STATE_SENDING_STOP, pendingEvernt=0x%lu ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->pending_events, host->dma_name);
+
+ if (!rk29_sdmmc_test_and_clear_pending(host, EVENT_CMD_COMPLETE))
+ break;
+
+ rk29_sdmmc_command_complete(host, host->cmd);
+ del_timer_sync(&host->request_timer); //delete the timer for INT_CMD_DONE int CMD12
+ rk29_sdmmc_request_end(host, host->cmd);
+
+ host->complete_done = 3;
+ break;
+ }
+
+ }
+
+ pending_flag = (host->complete_done > 0) && (host->retryfunc<50) \
+ && (rk29_sdmmc_test_pending(host, EVENT_CMD_COMPLETE)|| rk29_sdmmc_test_pending(host, EVENT_DATA_COMPLETE) );
+ if(pending_flag)
+ {
+ xbwprintk(7, "%s..%d... cmd=%d(arg=0x%x),completedone=%d, retrycount=%d, doneflag=%d, \n \
+ host->state=0x%x, switchstate=%x, \n \
+ pendingEvent=0x%lu, completeEvents=0x%lu, \n \
+ mrqCMD=%d, arg=0x%x \n ====xbw[%s]====\n",\
+
+ __FUNCTION__, __LINE__,host->cmd->opcode, host->cmd->arg, host->complete_done,\
+ host->retryfunc, host->mmc->doneflag,host->state, state, \
+ host->pending_events,host->completed_events,\
+ host->mrq->cmd->opcode, host->mrq->cmd->arg, host->dma_name);
+
+ cpu_relax();
+ }
+
+ } while(pending_flag && ++host->retryfunc); //while(0);
+
+ if(0!=stopflag)
+ {
+ if(host->cmd->error)
+ xbwprintk(3,"%d: call send_stop_cmd== %d, completedone=%d, doneflag=%d, hoststate=%x, statusReg=0x%x \n", \
+ __LINE__,stopflag, host->complete_done, host->mmc->doneflag, state, rk29_sdmmc_read(host->regs, SDMMC_STATUS));
+
+ state = STATE_SENDING_CMD;
+ send_stop_cmd(host); //Moidfyed by xbw at 2011-09-08
+ }
+
+ host->state = state;
+
+ if(0==host->complete_done)
+ {
+ host->errorstep = 0xf2;
+ spin_unlock_irqrestore(&host->lock, iflags);
+ return;
}
+ host->errorstep = 0xf3;
+ host->state = STATE_IDLE;
+
+ if(host->mrq && host->mmc->doneflag)
+ {
+ host->mmc->doneflag = 0;
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ mmc_request_done(host->mmc, host->mrq);
+ }
+ else
+ {
+ spin_unlock_irqrestore(&host->lock, iflags);
+ }
}
-inline static void rk29_sdmmc_pull_data(struct rk29_sdmmc *host,void *buf,int cnt)
+
+static inline void rk29_sdmmc_cmd_interrupt(struct rk29_sdmmc *host, u32 status)
{
- u32* pData = (u32*)buf;
+ u32 multi, unit;
- if (cnt % 4 != 0)
- cnt = (cnt>>2) +1;
- else
- cnt = cnt >> 2;
- while (cnt > 0) {
- *pData++ = rk29_sdmmc_read(host->regs, SDMMC_DATA);
- cnt--;
- }
-}
-
-static void rk29_sdmmc_read_data_pio(struct rk29_sdmmc *host)
-{
- struct scatterlist *sg = host->sg;
- void *buf = sg_virt(sg);
- unsigned int offset = host->pio_offset;
- struct mmc_data *data = host->data;
- u32 status;
- unsigned int nbytes = 0,len,old_len,count =0;
-
- do {
- len = SDMMC_GET_FCNT(rk29_sdmmc_read(host->regs, SDMMC_STATUS)) << 2;
- if(count == 0)
- old_len = len;
- if (likely(offset + len <= sg->length)) {
- rk29_sdmmc_pull_data(host, (void *)(buf + offset),len);
- offset += len;
- nbytes += len;
- if (offset == sg->length) {
- flush_dcache_page(sg_page(sg));
- host->sg = sg = sg_next(sg);
- if (!sg)
- goto done;
- offset = 0;
- buf = sg_virt(sg);
- }
- } else {
- unsigned int remaining = sg->length - offset;
- rk29_sdmmc_pull_data(host, (void *)(buf + offset),remaining);
- nbytes += remaining;
-
- flush_dcache_page(sg_page(sg));
- host->sg = sg = sg_next(sg);
- if (!sg)
- goto done;
- offset = len - remaining;
- buf = sg_virt(sg);
- rk29_sdmmc_pull_data(host, buf,offset);
- nbytes += offset;
- }
+ host->cmd_status |= status;
+ host->errorstep = 0xfc;
+ if((MMC_STOP_TRANSMISSION != host->cmd->opcode) && (host->cmdr & SDMMC_CMD_DAT_EXP))
+ {
+ unit = 3*1024*1024;
+ multi = rk29_sdmmc_read(host->regs, SDMMC_BYTCNT)/unit;
+ multi += ((rk29_sdmmc_read(host->regs, SDMMC_BYTCNT)%unit) ? 1 :0 );
+ multi = (multi>0) ? multi : 1;
+ multi += (host->cmd->retries>2)?2:host->cmd->retries;
+ mod_timer(&host->DTO_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_WAIT_DTO_INTERNVAL*multi));//max wait 8s larger
+ }
+
+ smp_wmb();
+ rk29_sdmmc_set_pending(host, EVENT_CMD_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+}
- status = rk29_sdmmc_read(host->regs, SDMMC_MINTSTS);
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_RXDR); // clear RXDR interrupt
- if (status & RK29_SDMMC_DATA_ERROR_FLAGS) {
- host->data_status = status;
- if(data)
- data->bytes_xfered += nbytes;
- else
- dev_info(&host->pdev->dev, "%s %d host data NULL err",__FUNCTION__,__LINE__);
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_DATA_ERROR);
- tasklet_schedule(&host->tasklet);
- return;
- }
- count ++;
- } while (status & SDMMC_INT_RXDR); // if the RXDR is ready let read again
- len = SDMMC_GET_FCNT(rk29_sdmmc_read(host->regs, SDMMC_STATUS));
- host->pio_offset = offset;
- if(data)
- data->bytes_xfered += nbytes;
- else
- dev_info(&host->pdev->dev, "%s %d host data NULL err",__FUNCTION__,__LINE__);
- return;
+static irqreturn_t rk29_sdmmc_interrupt(int irq, void *dev_id)
+{
+ struct rk29_sdmmc *host = dev_id;
+ u32 status, pending;
+ bool present;
+ bool present_old;
+ unsigned long iflags;
-done:
- if(data)
- data->bytes_xfered += nbytes;
- else
- dev_info(&host->pdev->dev, "%s %d host data NULL err",__FUNCTION__,__LINE__);
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_XFER_COMPLETE);
-}
-
-static void rk29_sdmmc_write_data_pio(struct rk29_sdmmc *host)
-{
- struct scatterlist *sg = host->sg;
- void *buf = sg_virt(sg);
- unsigned int offset = host->pio_offset;
- struct mmc_data *data = host->data;
- u32 status;
- unsigned int nbytes = 0,len;
-
- do {
-
- len = SDMMC_FIFO_SZ - (SDMMC_GET_FCNT(rk29_sdmmc_read(host->regs, SDMMC_STATUS)) << 2);
- if (likely(offset + len <= sg->length)) {
- rk29_sdmmc_push_data(host, (void *)(buf + offset),len);
-
- offset += len;
- nbytes += len;
- if (offset == sg->length) {
- host->sg = sg = sg_next(sg);
- if (!sg)
- goto done;
-
- offset = 0;
- buf = sg_virt(sg);
- }
- } else {
- unsigned int remaining = sg->length - offset;
- rk29_sdmmc_push_data(host, (void *)(buf + offset), remaining);
- nbytes += remaining;
-
- host->sg = sg = sg_next(sg);
- if (!sg) {
- goto done;
- }
-
- offset = len - remaining;
- buf = sg_virt(sg);
- rk29_sdmmc_push_data(host, (void *)buf, offset);
- nbytes += offset;
- }
+ spin_lock_irqsave(&host->lock, iflags);
- status = rk29_sdmmc_read(host->regs, SDMMC_MINTSTS);
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_TXDR); // clear RXDR interrupt
- if (status & RK29_SDMMC_DATA_ERROR_FLAGS) {
- host->data_status = status;
- if(data)
- data->bytes_xfered += nbytes;
- else
- dev_info(&host->pdev->dev, "%s %d host data NULL err",__FUNCTION__,__LINE__);
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_DATA_ERROR);
- tasklet_schedule(&host->tasklet);
- return;
- }
- } while (status & SDMMC_INT_TXDR); // if TXDR, let write again
+ status = rk29_sdmmc_read(host->regs, SDMMC_RINTSTS);
+ pending = rk29_sdmmc_read(host->regs, SDMMC_MINTSTS);// read only mask reg
+ if (!pending)
+ {
+ goto Exit_INT;
+ }
- host->pio_offset = offset;
- if(data)
- data->bytes_xfered += nbytes;
- else
- dev_info(&host->pdev->dev, "%s %d host data NULL err",__FUNCTION__,__LINE__);
- return;
-done:
- if(data)
- data->bytes_xfered += nbytes;
- else
- dev_info(&host->pdev->dev, "%s %d host data NULL err",__FUNCTION__,__LINE__);
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_XFER_COMPLETE);
-}
+ if(pending & SDMMC_INT_CD)
+ {
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, SDMMC_INT_CD); // clear sd detect int
+ present = rk29_sdmmc_get_cd(host->mmc);
+ present_old = test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+
+ if(present != present_old)
+ {
+ printk("\n******************\n%s:INT_CD=0x%x,INT-En=%d,hostState=%d, present Old=%d ==> New=%d ==xbw[%s]==\n",\
+ __FUNCTION__, pending, host->mmc->re_initialized_flags, host->state, present_old, present, host->dma_name);
+
+ rk28_send_wakeup_key(); //wake up backlight
+ host->error_times = 0;
+
+ #if 1
+ del_timer(&host->request_timer);
+ del_timer(&host->DTO_timer);
+ rk29_sdmmc_dealwith_timeout(host);
+ #endif
+
+ if(present)
+ {
+ set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+
+ if(host->mmc->re_initialized_flags)
+ {
+ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_REMOVAL_DELAY));
+ }
+ else
+ {
+ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(RK29_SDMMC_REMOVAL_DELAY*2));
+ }
+ }
+ else
+ {
+ clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ host->mmc->re_initialized_flags = 0;
+
+ mmc_detect_change(host->mmc, 200);
+ }
-static void rk29_sdmmc_cmd_interrupt(struct rk29_sdmmc *host, u32 status)
-{
- if(!host->cmd_status)
- host->cmd_status = status;
+ }
+
+ goto Exit_INT;
+
+ }
+
+
+ if (pending & SDMMC_INT_CMD_DONE) {
+
+ xbwprintk(6, "%s..%d.. CMD%d INT_CMD_DONE INT=0x%x ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->cmd->opcode,pending, host->dma_name);
+
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_CMD_DONE); // clear interrupt
+
+ rk29_sdmmc_cmd_interrupt(host, status);
+
+ goto Exit_INT;
+ }
+
+ if(pending & SDMMC_INT_SDIO)
+ {
+ xbwprintk(7, "%s..%d.. INT_SDIO INT=0x%x ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, pending, host->dma_name);
+
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_SDIO);
+ mmc_signal_sdio_irq(host->mmc);
+
+ goto Exit_INT;
+ }
+
+
+ if(pending & SDMMC_INT_RTO)
+ {
+ xbwprintk(7, "%s..%d.. CMD%d CMD_ERROR_FLAGS INT=0x%x ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, host->cmd->opcode,pending, host->dma_name);
+
+ //rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_RTO);
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,0xFFFFFFFF); //Modifyed by xbw at 2011-08-15
+ host->cmd_status = status;
+ smp_wmb();
+ rk29_sdmmc_set_pending(host, EVENT_CMD_COMPLETE);
+
+ if(!(pending & SDMMC_INT_CMD_DONE))
+ tasklet_schedule(&host->tasklet);
+
+ goto Exit_INT;
+ }
+
+
+ if(pending & SDMMC_INT_HLE)
+ {
+ printk("%s: Error due to hardware locked. Please check your hardware. INT=0x%x, CMD%d(arg=0x%x, retries=%d)==xbw[%s]==\n",\
+ __FUNCTION__, pending,host->cmd->opcode, host->cmd->arg, host->cmd->retries, host->dma_name);
+
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_HLE);
+ goto Exit_INT;
+ }
+
+
+ if(pending & SDMMC_INT_DTO)
+ {
+ xbwprintk(7, "%s..%d.. CMD%d INT_DTO INT=0x%x ==xbw[%s]==\n", \
+ __FUNCTION__, __LINE__,host->cmd->opcode,pending, host->dma_name);
+
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_DTO);
+ del_timer(&host->DTO_timer); //delete the timer for INT_DTO
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_CMD_COMPLETE);
- tasklet_schedule(&host->tasklet);
-}
+ host->data_status |= status;
-#if 0
-static int rk29_sdmmc1_card_get_cd(struct mmc_host *mmc)
-{
- struct rk29_sdmmc *host = mmc_priv(mmc);
- struct rk29_sdmmc_platform_data *pdata = host->pdev->dev.platform_data;
- return gpio_get_value(pdata->detect_irq);
-}
+ smp_wmb();
-static int rk29_sdmmc1_card_change_cd_trigger_type(struct mmc_host *mmc, unsigned int type)
-{
- struct rk29_sdmmc *host = mmc_priv(mmc);
- struct rk29_sdmmc_platform_data *pdata = host->pdev->dev.platform_data;
- return set_irq_type(gpio_to_irq(pdata->detect_irq), type);
-}
+ rk29_sdmmc_set_pending(host, EVENT_DATA_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+ goto Exit_INT;
+ }
-static irqreturn_t rk29_sdmmc1_card_detect_interrupt(int irq, void *dev_id)
-{
- struct rk29_sdmmc *host = dev_id;
- bool present, present_old;
-
- present = rk29_sdmmc1_card_get_cd(host->mmc);
- present_old = test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- if (present != present_old) {
- if (present != 0) {
- set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- } else {
- clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- }
- mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(200));
- }
- rk29_sdmmc1_card_change_cd_trigger_type(host->mmc, (present ? IRQF_TRIGGER_FALLING: IRQF_TRIGGER_RISING));
-
- return IRQ_HANDLED;
+ if (pending & SDMMC_INT_FRUN)
+ {
+ printk("%s: INT=0x%x Oh!My God,let me see!What happened?Why?Where? CMD%d(arg=0x%x, retries=%d) ==xbw[%s]==\n", \
+ __FUNCTION__, pending, host->cmd->opcode, host->cmd->arg, host->cmd->retries,host->dma_name);
+
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_FRUN);
+ goto Exit_INT;
+ }
-}
+#if defined(CONFIG_ARCH_RK30)
+ if(pending & SDMMC_INT_UNBUSY)
+ {
+ // printk("%d..%s: ==test=== xbw======\n", __LINE__, __FUNCTION__);
+ // rk29_sdmmc_regs_printk(host);
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_UNBUSY);
+ goto Exit_INT;
+ }
#endif
-static irqreturn_t rk29_sdmmc_interrupt(int irq, void *dev_id)
-{
- struct rk29_sdmmc *host = dev_id;
- u32 status, pending, mask;
-// unsigned int pass_count = 0;
- bool present;
- bool present_old;
+ if (pending & SDMMC_INT_RXDR)
+ {
+ xbwprintk(6, "%s..%d.. SDMMC_INT_RXDR INT=0x%x ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, pending, host->dma_name);
- spin_lock(&host->lock);
-// do {
- status = rk29_sdmmc_read(host->regs, SDMMC_RINTSTS);
- mask = rk29_sdmmc_read(host->regs, SDMMC_MINTSTS);// read only mask reg
- pending = status & mask;
-
-// if (!pending)
-// break;
- if(pending & SDMMC_INT_CD) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, SDMMC_INT_CD); // clear sd detect int
- present = rk29_sdmmc_get_cd(host->mmc);
- present_old = test_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- if (present != 0) {
- dev_info(&host->pdev->dev, "card detect interrupt,card present \n");
- set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- if(present == present_old)
- mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(2000));
- else
- mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(200));
- } else {
- dev_info(&host->pdev->dev, "card detect interrupt,card not present \n");
- clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(10));
- }
- }
- if(pending & RK29_SDMMC_CMD_ERROR_FLAGS) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,RK29_SDMMC_CMD_ERROR_FLAGS); // clear interrupt
- host->cmd_status = status;
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_CMD_COMPLETE);
- if(host->cmd == NULL) {
- dev_info(&host->pdev->dev, "host->cmd == NULL\n");
- }
- else
- dev_info(&host->pdev->dev, "cmd transfer error(int status 0x%x cmd %d host->state %d pending_events %d)\n",
- status,host->cmd->opcode,host->state,host->pending_events);
- tasklet_schedule(&host->tasklet);
- }
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_RXDR); // clear interrupt
+ rk29_sdmmc_do_pio_read(host);
+ }
- if (pending & RK29_SDMMC_DATA_ERROR_FLAGS) { // if there is an error, let report DATA_ERROR
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,RK29_SDMMC_DATA_ERROR_FLAGS); // clear interrupt
- host->data_status = status;
- smp_wmb();
- rk29_sdmmc_set_pending(host, EVENT_DATA_ERROR);
- tasklet_schedule(&host->tasklet);
- dev_info(&host->pdev->dev, "data transfer error(int status 0x%x host->state %d pending_events %d pending=0x%x)\n",
- status,host->state,host->pending_events,pending);
- }
+ if (pending & SDMMC_INT_TXDR)
+ {
+ xbwprintk(6, "%s..%d.. SDMMC_INT_TXDR INT=0x%x ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, pending, host->dma_name);
- if(pending & SDMMC_INT_DTO) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_DTO); // clear interrupt
- if (!host->data_status)
- host->data_status = status;
- smp_wmb();
- if(host->dir_status == RK29_SDMMC_RECV_STATUS) {
- if(host->sg != NULL)
- rk29_sdmmc_read_data_pio(host);
- }
- rk29_sdmmc_set_pending(host, EVENT_DATA_COMPLETE);
- tasklet_schedule(&host->tasklet);
- }
+ rk29_sdmmc_do_pio_write(host);
- if (pending & SDMMC_INT_RXDR) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_RXDR); // clear interrupt
- if(host->sg)
- rk29_sdmmc_read_data_pio(host);
- }
+ rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_TXDR); // clear interrupt
+ }
- if (pending & SDMMC_INT_TXDR) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_TXDR); // clear interrupt
- if(host->sg)
- rk29_sdmmc_write_data_pio(host);
-
- }
+Exit_INT:
- if (pending & SDMMC_INT_CMD_DONE) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_CMD_DONE); // clear interrupt
- rk29_sdmmc_cmd_interrupt(host, status);
- }
- if(pending & SDMMC_INT_SDIO) {
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_SDIO);
- mmc_signal_sdio_irq(host->mmc);
- }
-// } while (pass_count++ < 5);
- spin_unlock(&host->lock);
+ spin_unlock_irqrestore(&host->lock, iflags);
return IRQ_HANDLED;
-// return pass_count ? IRQ_HANDLED : IRQ_NONE;
}
/*
*/
static void rk29_sdmmc_detect_change(unsigned long data)
{
- struct mmc_request *mrq;
struct rk29_sdmmc *host = (struct rk29_sdmmc *)data;
+
+ if(!host->mmc)
+ return;
smp_rmb();
- if (test_bit(RK29_SDMMC_SHUTDOWN, &host->flags))
- return;
- spin_lock(&host->lock);
- /* Clean up queue if present */
- mrq = host->mrq;
- if (mrq) {
- if (mrq == host->curr_mrq) {
-#if 0 //TODO ,ºóÃæÒª°ÑÕâ¿é´¦ÀíÒƵ½tasklet,ʹÓÃ״̬»úÀ´×ö
- /* reset all blocks */
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- /* wait till resets clear */
- while (rk29_sdmmc_read(host->regs, SDMMC_CTRL) & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- /* FIFO threshold settings */
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, rk29_sdmmc_read(host->regs, SDMMC_CTRL) | SDMMC_CTRL_INT_ENABLE);
-#endif
- host->data = NULL;
- host->cmd = NULL;
- dev_info(&host->pdev->dev, "%s >> %s >> %d state=%d\n", __FILE__, __FUNCTION__,__LINE__,host->state);
-
- switch (host->state) {
- case STATE_IDLE:
- break;
- case STATE_SENDING_CMD:
- mrq->cmd->error = -ENOMEDIUM;
- if (!mrq->data)
- break;
- /* fall through */
- case STATE_SENDING_DATA:
- if(mrq->data)
- mrq->data->error = -ENOMEDIUM;
- rk29_sdmmc_stop_dma(host);
- break;
- case STATE_DATA_BUSY:
- case STATE_DATA_ERROR:
- if (mrq->data && mrq->data->error == -EINPROGRESS)
- mrq->data->error = -ENOMEDIUM;
- if (!mrq->stop)
- break;
- /* fall through */
- case STATE_SENDING_STOP:
- mrq->stop->error = -ENOMEDIUM;
- break;
- }
-
- rk29_sdmmc_request_end(host, mrq);
- } else {
- if (host->queue_node.next && host->queue_node.prev)
- list_del(&host->queue_node);
- mrq->cmd->error = -ENOMEDIUM;
- if (mrq->data) {
- mrq->data->bytes_xfered = 0;
- mrq->data->error = -ENOMEDIUM;
- }
- if (mrq->stop)
- mrq->stop->error = -ENOMEDIUM;
- spin_unlock(&host->lock);
- mmc_request_done(host->mmc, mrq);
- spin_lock(&host->lock);
- }
-
-
- /* reset all blocks */
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- /* wait till resets clear */
- dev_info(&host->pdev->dev, "mmc_detect_change: ctrl reset ");
- while (rk29_sdmmc_read(host->regs, SDMMC_CTRL) & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- printk("done\n");
- rk29_sdmmc_write(host->regs, SDMMC_CTRL, rk29_sdmmc_read(host->regs, SDMMC_CTRL) | SDMMC_CTRL_INT_ENABLE);
- }
- spin_unlock(&host->lock);
-
+
+ if((RK29_CTRL_SDMMC_ID == host->pdev->id) && rk29_sdmmc_get_cd(host->mmc))
+ {
+ host->mmc->re_initialized_flags =1;
+ }
+
mmc_detect_change(host->mmc, 0);
+
}
static void rk29_sdmmc1_check_status(unsigned long data)
struct rk29_sdmmc_platform_data *pdata = host->pdev->dev.platform_data;
unsigned int status;
- status = pdata->status(mmc_dev(host->mmc));
-
- if (status ^ host->oldstatus)
+ status = pdata->status(mmc_dev(host->mmc));
+
+ pr_info("%s: slot status change detected(%d-%d)\n",mmc_hostname(host->mmc), host->oldstatus, status);
+
+ if (status ^ host->oldstatus)
+ {
+ if (status)
{
- pr_info("%s: slot status change detected(%d-%d)\n",mmc_hostname(host->mmc), host->oldstatus, status);
- if (status) {
- set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(200));
- }
- else {
- clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- rk29_sdmmc_detect_change((unsigned long)host);
- }
+ rk29_sdmmc_hw_init(host);
+ set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(200));
+ }
+ else
+ {
+ clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ rk29_sdmmc_detect_change((unsigned long)host);
}
+ }
- host->oldstatus = status;
+ host->oldstatus = status;
}
static void rk29_sdmmc1_status_notify_cb(int card_present, void *dev_id)
{
struct rk29_sdmmc *host = dev_id;
- printk(KERN_INFO "%s, card_present %d\n", mmc_hostname(host->mmc), card_present);
+ //printk(KERN_INFO "%s, card_present %d\n", mmc_hostname(host->mmc), card_present);
rk29_sdmmc1_check_status((unsigned long)host);
}
+
static int rk29_sdmmc_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
int irq;
int ret = 0;
+ /* must have platform data */
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data missing\n");
+ ret = -ENODEV;
+ host->errorstep = 0x87;
+ goto out;
+ }
+
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
+ {
+ host->errorstep = 0x88;
return -ENXIO;
+ }
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
mmc = mmc_alloc_host(sizeof(struct rk29_sdmmc), &pdev->dev);
if (!mmc)
- return -ENOMEM;
+ {
+ host->errorstep = 0x89;
+ ret = -ENOMEM;
+ goto rel_regions;
+ }
+
host = mmc_priv(mmc);
host->mmc = mmc;
- host->pdev = pdev;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "Platform data missing\n");
- ret = -ENODEV;
- goto err_freehost;
- }
+ host->pdev = pdev;
+
+ host->ctype = 0; // set default 1 bit mode
+ host->errorstep = 0;
+ host->bus_mode = 5;
+ host->old_cmd = 100;
+ host->clock =0;
+ host->old_div = 0xFF;
+ host->error_times = 0;
+ host->state = STATE_IDLE;
+ host->complete_done = 0;
+ host->retryfunc = 0;
+ host->mrq = NULL;
+ host->new_mrq = NULL;
+
+#ifdef CONFIG_PM
+ host->gpio_det = pdata->detect_irq;
+#endif
+ host->set_iomux = pdata->set_iomux;
+
if(pdata->io_init)
pdata->io_init();
+
spin_lock_init(&host->lock);
+
+#ifdef RK29_SDMMC_LIST_QUEUE
INIT_LIST_HEAD(&host->queue);
- ret = -ENOMEM;
- host->regs = ioremap(regs->start, regs->end - regs->start);
- if (!host->regs)
- goto err_freemap;
- memcpy(host->dma_name, pdata->dma_name, 8);
- host->use_dma = pdata->use_dma;
- if(host->use_dma){
- if(strncmp(host->dma_name, "sdio", strlen("sdio")) == 0){
- rk29_dma_request(DMACH_SDIO, &rk29_dma_sdio1_client, NULL);
- host->dma_chn = DMACH_SDIO;
- }
- if(strncmp(host->dma_name, "sd_mmc", strlen("sd_mmc")) == 0){
- rk29_dma_request(DMACH_SDMMC, &rk29_dma_sdmmc0_client, NULL);
- host->dma_chn = DMACH_SDMMC;
- }
- rk29_dma_config(host->dma_chn, 16);
- rk29_dma_set_buffdone_fn(host->dma_chn, rk29_sdmmc_dma_complete);
- host->dma_addr = regs->start + SDMMC_DATA;
- }
+#endif
+
host->clk = clk_get(&pdev->dev, "mmc");
- clk_set_rate(host->clk,52000000);
+
+#if RK29_SDMMC_DEFAULT_SDIO_FREQ
+ clk_set_rate(host->clk,SDHC_FPP_FREQ);
+#else
+ if(RK29_CTRL_SDMMC_ID== host->pdev->id)
+ clk_set_rate(host->clk,SDHC_FPP_FREQ);
+ else
+ clk_set_rate(host->clk,RK29_MAX_SDIO_FREQ);
+
+#endif
+
clk_enable(host->clk);
clk_enable(clk_get(&pdev->dev, "hclk_mmc"));
- host->bus_hz = clk_get_rate(host->clk); ///40000000; ////cgu_get_clk_freq(CGU_SB_SD_MMC_CCLK_IN_ID);
-
- /* reset all blocks */
- rk29_sdmmc_write(host->regs, SDMMC_CTRL,(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- /* wait till resets clear */
- while (rk29_sdmmc_read(host->regs, SDMMC_CTRL) & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
- /* Clear the interrupts for the host controller */
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK, 0); // disable all mmc interrupt first
- /* Put in max timeout */
- rk29_sdmmc_write(host->regs, SDMMC_TMOUT, 0xFFFFFFFF);
-
- /* FIFO threshold settings */
- rk29_sdmmc_write(host->regs, SDMMC_FIFOTH, ((0x3 << 28) | (0x0f << 16) | (0x10 << 0))); // RXMark = 15, TXMark = 16, DMA Size = 16
- /* disable clock to CIU */
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA,0);
- rk29_sdmmc_write(host->regs, SDMMC_CLKSRC,0);
- rk29_sdmmc_write(host->regs, SDMMC_PWREN, 1);
+
+ ret = -ENOMEM;
+ host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!host->regs)
+ {
+ host->errorstep = 0x8A;
+ goto err_freemap;
+ }
+
+ mmc->ops = &rk29_sdmmc_ops[pdev->id];
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
+#endif
+ mmc->f_min = FOD_FREQ;
+
+#if RK29_SDMMC_DEFAULT_SDIO_FREQ
+ mmc->f_max = SDHC_FPP_FREQ;
+#else
+ if(RK29_CTRL_SDMMC_ID== host->pdev->id)
+ {
+ mmc->f_max = SDHC_FPP_FREQ;
+ }
+ else
+ {
+ mmc->f_max = RK29_MAX_SDIO_FREQ;
+ }
+
+#endif
+ //mmc->ocr_avail = pdata->host_ocr_avail;
+ mmc->ocr_avail = MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31
+ | MMC_VDD_31_32|MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_34_35| MMC_VDD_35_36; ///set valid volage 2.7---3.6v
+ mmc->caps = pdata->host_caps;
+ mmc->re_initialized_flags = 1;
+ mmc->doneflag = 1;
+ mmc->sdmmc_host_hw_init = rk29_sdmmc_hw_init;
+
+ /*
+ * We can do SGIO
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37))
+ mmc->max_segs = 64;
+#else
+ mmc->max_phys_segs = 64;
+ mmc->max_hw_segs = 64;
+#endif
+
+ /*
+ * Block size can be up to 2048 bytes, but must be a power of two.
+ */
+ mmc->max_blk_size = 4095;
+
+ /*
+ * No limit on the number of blocks transferred.
+ */
+ mmc->max_blk_count = 4096;
+
+ /*
+ * Since we only have a 16-bit data length register, we must
+ * ensure that we don't exceed 2^16-1 bytes in a single request.
+ */
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; //8M bytes(2K*4K)
+
+ /*
+ * Set the maximum segment size. Since we aren't doing DMA
+ * (yet) we are only limited by the data length register.
+ */
+ mmc->max_seg_size = mmc->max_req_size;
+
tasklet_init(&host->tasklet, rk29_sdmmc_tasklet_func, (unsigned long)host);
- ret = request_irq(irq, rk29_sdmmc_interrupt, 0, dev_name(&pdev->dev), host);
- if (ret)
- goto err_dmaunmap;
-
-#if 0
- /* register sdmmc1 card detect interrupt route */
- if ((pdev->id == 1) && (pdata->detect_irq != 0)) {
- irq = gpio_to_irq(pdata->detect_irq);
- if (irq < 0) {
- dev_info(&host->pdev->dev, "%s: request gpio irq failed\n", __FUNCTION__);
- goto err_dmaunmap;
- }
+
+ /* Create card detect handler thread */
+ setup_timer(&host->detect_timer, rk29_sdmmc_detect_change,(unsigned long)host);
+ setup_timer(&host->request_timer,rk29_sdmmc_INT_CMD_DONE_timeout,(unsigned long)host);
+ setup_timer(&host->DTO_timer,rk29_sdmmc_INT_DTO_timeout,(unsigned long)host);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ {
+ host->errorstep = 0x8B;
+ ret = -EINVAL;
+ goto err_freemap;
+ }
+
+ memcpy(host->dma_name, pdata->dma_name, 8);
+ host->use_dma = pdata->use_dma;
+
+ xbwprintk(7,"%s..%s..%d..*********** Bus clock= %d Khz ====xbw[%s]===\n",\
+ __FILE__, __FUNCTION__,__LINE__,clk_get_rate(host->clk)/1000, host->dma_name);
+
+ /*DMA init*/
+ if(host->use_dma)
+ {
+ host->dma_info = rk29_sdmmc_dma_infos[host->pdev->id];
+ ret = rk29_dma_request(host->dma_info.chn, &(host->dma_info.client), NULL);
+ if (ret < 0)
+ {
+ printk("%s..%d...rk29_dma_request error=%d.===xbw[%s]====\n", \
+ __FUNCTION__, __LINE__,ret, host->dma_name);
+ host->errorstep = 0x97;
+ goto err_freemap;
+ }
- ret = request_irq(irq, rk29_sdmmc1_card_detect_interrupt, IRQF_TRIGGER_RISING, dev_name(&pdev->dev), host);
- if (ret) {
- dev_info(&host->pdev->dev, "%s: sdmmc1 request detect interrupt failed\n", __FUNCTION__);
- goto err_dmaunmap;
- }
-
- }
-#endif
- /* setup sdmmc1 wifi card detect change */
- if (pdata->register_status_notify) {
- pdata->register_status_notify(rk29_sdmmc1_status_notify_cb, host);
+#if 0 //deal with the old API of DMA-module
+ ret = rk29_dma_config(host->dma_info.chn, 4);
+#else //deal with the new API of DMA-module
+ if(RK29_CTRL_SDMMC_ID== host->pdev->id)
+ {
+ ret = rk29_dma_config(host->dma_info.chn, 4, 16);
+ }
+ else
+ {
+ ret = rk29_dma_config(host->dma_info.chn, 4, 1);
}
+#endif
+ if(ret < 0)
+ {
+ printk("%s..%d.. rk29_dma_config error=%d ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, ret, host->dma_name);
+ host->errorstep = 0x98;
+ goto err_dmaunmap;
+ }
+
+ ret = rk29_dma_set_buffdone_fn(host->dma_info.chn, rk29_sdmmc_dma_complete);
+ if(ret < 0)
+ {
+ printk("%s..%d.. dma_set_buffdone_fn error=%d ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, ret, host->dma_name);
+ host->errorstep = 0x99;
+ goto err_dmaunmap;
+ }
+
+ host->dma_addr = regs->start + SDMMC_DATA;
+ }
+
+#if defined(CONFIG_SDMMC0_RK29_WRITE_PROTECT) || defined(CONFIG_SDMMC1_RK29_WRITE_PROTECT)
+ host->write_protect = pdata->write_prt;
+#endif
+
+ rk29_sdmmc_hw_init(host);
+
+ ret = request_irq(irq, rk29_sdmmc_interrupt, 0, dev_name(&pdev->dev), host);
+ if (ret)
+ {
+
+ printk("%s..%d.. request_irq error=%d ====xbw[%s]====\n", \
+ __FUNCTION__, __LINE__, ret, host->dma_name);
+ host->errorstep = 0x8C;
+ goto err_dmaunmap;
+ }
+
+ /* setup sdmmc1 wifi card detect change */
+ if (pdata->register_status_notify) {
+ pdata->register_status_notify(rk29_sdmmc1_status_notify_cb, host);
+ }
- /* Assume card is present initially */
+ if(RK29_CTRL_SDMMC_ID== host->pdev->id)
+ {
if(rk29_sdmmc_get_cd(host->mmc))
- set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ {
+ set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ }
else
- clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
-
- /* sdmmc1 wifi card slot status initially */
- if (pdata->status) {
- host->oldstatus = pdata->status(mmc_dev(host->mmc));
- if (host->oldstatus) {
- set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- }else {
- clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
- }
+ {
+ clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
}
+ }
+ else
+ {
+ #if defined(CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD)
+ if(0== host->pdev->id)
+ {
+ set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ }
+ #endif
+
+ #if defined(CONFIG_USE_SDMMC1_FOR_WIFI_DEVELOP_BOARD)
+ if(1== host->pdev->id)
+ {
+ set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ }
+ #endif
+ }
+
+
+ /* sdmmc1 wifi card slot status initially */
+ if (pdata->status) {
+ host->oldstatus = pdata->status(mmc_dev(host->mmc));
+ if (host->oldstatus) {
+ set_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ }else {
+ clear_bit(RK29_SDMMC_CARD_PRESENT, &host->flags);
+ }
+ }
+
+
+ platform_set_drvdata(pdev, mmc);
- platform_set_drvdata(pdev, host);
- mmc->ops = &rk29_sdmmc_ops[pdev->id];
- mmc->f_min = host->bus_hz/510;
- mmc->f_max = host->bus_hz/2; //2; ///20; //max f is clock to mmc_clk/2
- mmc->ocr_avail = pdata->host_ocr_avail;
- mmc->caps = pdata->host_caps;
- mmc->max_phys_segs = 64;
- mmc->max_hw_segs = 64;
- mmc->max_blk_size = 4095; //65536; /* SDMMC_BLKSIZ is 16 bits*/
- mmc->max_blk_count = 65535; ///512;
- mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
- mmc->max_seg_size = mmc->max_req_size;
-
mmc_add_host(mmc);
+
+#ifdef RK29_SDMMC_NOTIFY_REMOVE_INSERTION
+
+ globalSDhost[pdev->id] = (struct rk29_sdmmc *)host;
+ if(0== host->pdev->id)
+ {
+ rk29_sdmmc_progress_add_attr(pdev);
+ }
+#endif
+
#if defined (CONFIG_DEBUG_FS)
rk29_sdmmc_init_debugfs(host);
#endif
- /* Create card detect handler thread */
- setup_timer(&host->detect_timer, rk29_sdmmc_detect_change,(unsigned long)host);
- // enable interrupt for command done, data over, data empty, receive ready and error such as transmit, receive timeout, crc error
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
- if(host->use_dma)
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD);
- else
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | SDMMC_INT_TXDR | SDMMC_INT_RXDR | RK29_SDMMC_ERROR_FLAGS | SDMMC_INT_CD);
- rk29_sdmmc_write(host->regs, SDMMC_CTRL,SDMMC_CTRL_INT_ENABLE); // enable mci interrupt
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA,1);
- dev_info(&pdev->dev, "RK29 SDMMC controller at irq %d\n", irq);
+
+ printk(".Line%d..The End of SDMMC-probe %s ===xbw[%s]===\n\n", __LINE__, RK29_SDMMC_VERSION,host->dma_name);
return 0;
+
+
err_dmaunmap:
- if(host->use_dma){
- if(strncmp(host->dma_name, "sdio", strlen("sdio")) == 0)
- rk29_dma_free(DMACH_SDIO, &rk29_dma_sdio1_client);
- if(strncmp(host->dma_name, "sd_mmc", strlen("sd_mmc")) == 0)
- rk29_dma_free(DMACH_SDMMC, &rk29_dma_sdmmc0_client);
+ if(host->use_dma)
+ {
+ rk29_dma_free(host->dma_info.chn, &host->dma_info.client);
}
+
err_freemap:
iounmap(host->regs);
-err_freehost:
- kfree(host);
+
+rel_regions:
+ mmc_free_host(mmc);
+
+out:
+
return ret;
}
static int __exit rk29_sdmmc_remove(struct platform_device *pdev)
{
- struct rk29_sdmmc *host = platform_get_drvdata(pdev);
- rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, 0xFFFFFFFF);
- rk29_sdmmc_write(host->regs, SDMMC_INTMASK, 0); // disable all mmc interrupt first
-
- /* Shutdown detect IRQ and kill detect thread */
- del_timer_sync(&host->detect_timer);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct rk29_sdmmc *host;
+ struct resource *regs;
- /* Debugfs stuff is cleaned up by mmc core */
- set_bit(RK29_SDMMC_SHUTDOWN, &host->flags);
+ if (!mmc)
+ return -1;
+
+ host = mmc_priv(mmc);
+
smp_wmb();
- mmc_remove_host(host->mmc);
- mmc_free_host(host->mmc);
+ rk29_sdmmc_control_clock(host, 0);
- /* disable clock to CIU */
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA,0);
- rk29_sdmmc_write(host->regs, SDMMC_CLKSRC,0);
-
+ /* Shutdown detect IRQ and kill detect thread */
+ del_timer_sync(&host->detect_timer);
+ del_timer_sync(&host->request_timer);
+ del_timer_sync(&host->DTO_timer);
+
+ tasklet_disable(&host->tasklet);
free_irq(platform_get_irq(pdev, 0), host);
- if(host->use_dma){
- if(strncmp(host->dma_name, "sdio", strlen("sdio")) == 0)
- rk29_dma_free(DMACH_SDIO, &rk29_dma_sdio1_client);
- if(strncmp(host->dma_name, "sd_mmc", strlen("sd_mmc")) == 0)
- rk29_dma_free(DMACH_SDMMC, &rk29_dma_sdmmc0_client);
+ if(host->use_dma)
+ {
+ rk29_dma_free(host->dma_info.chn, &host->dma_info.client);
}
+
+ mmc_remove_host(mmc);
+
iounmap(host->regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(regs->start,resource_size(regs));
+
+ mmc_free_host(mmc);
+ platform_set_drvdata(pdev, NULL);
- kfree(host);
return 0;
}
-static int rk29_sdmmc_suspend(struct platform_device *pdev, pm_message_t state)
+
+
+#ifdef CONFIG_PM
+
+#if defined(CONFIG_ARCH_RK29)
+static irqreturn_t det_keys_isr(int irq, void *dev_id)
+{
+ struct rk29_sdmmc *host = dev_id;
+ dev_info(&host->pdev->dev, "sd det_gpio changed(%s), send wakeup key!\n",
+ gpio_get_value(RK29_PIN2_PA2)?"removed":"insert");
+ rk29_sdmmc_detect_change((unsigned long)dev_id);
+
+ return IRQ_HANDLED;
+}
+
+static int rk29_sdmmc_sdcard_suspend(struct rk29_sdmmc *host)
{
int ret = 0;
-#ifdef CONFIG_PM
- struct rk29_sdmmc *host = platform_get_drvdata(pdev);
+ rk29_mux_api_set(GPIO2A2_SDMMC0DETECTN_NAME, GPIO2L_GPIO2A2);
+ gpio_request(RK29_PIN2_PA2, "sd_detect");
+ gpio_direction_input(RK29_PIN2_PA2);
+
+ host->gpio_irq = gpio_to_irq(RK29_PIN2_PA2);
+ ret = request_irq(host->gpio_irq, det_keys_isr,
+ (gpio_get_value(RK29_PIN2_PA2))?IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING,
+ "sd_detect",
+ host);
+
+ enable_irq_wake(host->gpio_irq);
- dev_info(&host->pdev->dev, "Enter rk29_sdmmc_suspend\n");
- if(host->mmc && (strncmp(host->dma_name, "sdio", strlen("sdio")) != 0))
- ret = mmc_suspend_host(host->mmc, state);
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA, 0);
- clk_disable(host->clk);
-#endif
return ret;
}
+static void rk29_sdmmc_sdcard_resume(struct rk29_sdmmc *host)
+{
+ disable_irq_wake(host->gpio_irq);
+ free_irq(host->gpio_irq,host);
+ gpio_free(RK29_PIN2_PA2);
+ rk29_mux_api_set(GPIO2A2_SDMMC0DETECTN_NAME, GPIO2L_SDMMC0_DETECT_N);
+}
-static int rk29_sdmmc_resume(struct platform_device *pdev)
+#elif defined(CONFIG_ARCH_RK30)
+static irqreturn_t det_keys_isr(int irq, void *dev_id)
+{
+ struct rk29_sdmmc *host = dev_id;
+ dev_info(&host->pdev->dev, "sd det_gpio changed(%s), send wakeup key!\n",
+ gpio_get_value(RK30_PIN3_PB6)?"removed":"insert");
+ rk29_sdmmc_detect_change((unsigned long)dev_id);
+
+ return IRQ_HANDLED;
+}
+
+static int rk29_sdmmc_sdcard_suspend(struct rk29_sdmmc *host)
{
int ret = 0;
-#ifdef CONFIG_PM
- struct rk29_sdmmc *host = platform_get_drvdata(pdev);
+ rk29_mux_api_set(GPIO3B6_SDMMC0DETECTN_NAME, GPIO3B_GPIO3B6);
+ gpio_request(RK30_PIN3_PB6, "sd_detect");
+ gpio_direction_input(RK30_PIN3_PB6);
+
+ host->gpio_irq = gpio_to_irq(RK30_PIN3_PB6);
+ ret = request_irq(host->gpio_irq, det_keys_isr,
+ (gpio_get_value(RK30_PIN3_PB6))?IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING,
+ "sd_detect",
+ host);
+
+ enable_irq_wake(host->gpio_irq);
- dev_info(&host->pdev->dev, "Exit rk29_sdmmc_suspend\n");
- clk_enable(host->clk);
- rk29_sdmmc_write(host->regs, SDMMC_CLKENA, 1);
- if(host->mmc && (strncmp(host->dma_name, "sdio", strlen("sdio")) != 0))
- ret = mmc_resume_host(host->mmc);
+ return ret;
+}
+static void rk29_sdmmc_sdcard_resume(struct rk29_sdmmc *host)
+{
+ disable_irq_wake(host->gpio_irq);
+ free_irq(host->gpio_irq,host);
+ gpio_free(RK30_PIN3_PB6);
+ rk29_mux_api_set(GPIO3B6_SDMMC0DETECTN_NAME, GPIO3B_SDMMC0_DETECT_N);
+}
+
+#endif
+
+
+static int rk29_sdmmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct rk29_sdmmc *host = mmc_priv(mmc);
+ int ret = 0;
+
+ if(host && host->pdev && (RK29_CTRL_SDMMC_ID == host->pdev->id)) //only the SDMMC0 have suspend-resume; noted by xbw
+ {
+ if (mmc)
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ ret = mmc_suspend_host(mmc);
+#else
+ ret = mmc_suspend_host(mmc, state);
#endif
+
+ if(rk29_sdmmc_sdcard_suspend(host) < 0)
+ dev_info(&host->pdev->dev, "rk29_sdmmc_sdcard_suspend error\n");
+ }
+
+ return ret;
+}
+
+static int rk29_sdmmc_resume(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct rk29_sdmmc *host = mmc_priv(mmc);
+ int ret = 0;
+
+ if(host && host->pdev && (RK29_CTRL_SDMMC_ID == host->pdev->id)) //only the SDMMC0 have suspend-resume; noted by xbw
+ {
+ if (mmc)
+ {
+ rk29_sdmmc_sdcard_resume(host);
+ ret = mmc_resume_host(mmc);
+ }
+ }
+
return ret;
}
+#else
+#define rk29_sdmmc_suspend NULL
+#define rk29_sdmmc_resume NULL
+#endif
+
static struct platform_driver rk29_sdmmc_driver = {
.suspend = rk29_sdmmc_suspend,
.resume = rk29_sdmmc_resume,
module_exit(rk29_sdmmc_exit);
MODULE_DESCRIPTION("Rk29 Multimedia Card Interface driver");
-MODULE_AUTHOR("Rockchips");
+MODULE_AUTHOR("xbw@rock-chips.com");
MODULE_LICENSE("GPL v2");