update sdmmc driver for suspend bug
authorkfx <kfx@rock-chips.com>
Fri, 22 Apr 2011 03:21:41 +0000 (11:21 +0800)
committerkfx <kfx@rock-chips.com>
Fri, 22 Apr 2011 03:21:41 +0000 (11:21 +0800)
drivers/mmc/host/rk29_sdmmc.c

index 091756dd606a76e6b7a3815d33fb3faa91beed0d..9129aaf806c8db1e957f8329ad92e447aa304d79 100755 (executable)
@@ -13,6 +13,7 @@
  * 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>
@@ -34,6 +35,7 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/card.h>
+#include <linux/earlysuspend.h>
 
 #include <mach/board.h>
 #include <mach/rk29_iomap.h>
@@ -63,7 +65,7 @@ enum {
        MRQ_REQUEST_START = 0,
        MRQ_INT_CMD_DONE,  //1
        MRQ_INT_CMD_ERR,  //2
-       MRQ_INT_DATE_DONE,  //3
+       MRQ_INT_DATA_DONE,  //3
        MRQ_INT_DATA_ERR,  //4
        MRQ_INT_CD,  //5
        MRQ_INT_SDIO,  //6
@@ -157,7 +159,7 @@ struct rk29_sdmmc {
        void __iomem                            *regs;
 
        struct mmc_host                         *mmc;
-       struct work_struct              work;
+       struct delayed_work                     work;
        struct rk29_sdmmc_dma_info      dma_info;
        struct tasklet_struct           tasklet;
        struct mmc_request                      *mrq;
@@ -169,6 +171,9 @@ struct rk29_sdmmc {
        dma_addr_t                              dma_addr;
        
        int (*get_wifi_cd)(struct device *);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       struct early_suspend            early_suspend;
+#endif
 };
 
 static void rk29_sdmmc_write(unsigned char  __iomem    *regbase, unsigned int regOff,unsigned int val)
@@ -403,6 +408,7 @@ static int rk29_sdmmc_reset_ctrl(struct rk29_sdmmc *host)
 
        if(tmo > 0) {
                rk29_sdmmc_set_mrq_status(host, MRQ_RESET_CTRL_DONE);
+               host->is_init = 1;
                return 0;
        }
        else {
@@ -448,26 +454,33 @@ static int send_stop_cmd(struct rk29_sdmmc *host)
 
 static void rk29_sdmmc_request_done(struct rk29_sdmmc *host,struct mmc_request *mrq)
 {
+       int tmo = RK29_SDMMC_TMO_COUNT;
+
+       spin_unlock(&host->lock);
        rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, ~SDMMC_INT_SDIO);
        rk29_sdmmc_write(host->regs, SDMMC_INTMASK,
                rk29_sdmmc_read(host->regs, SDMMC_INTMASK) & 
                ~(SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS));
-       if(mrq->stop)
+       if(mrq->stop && !rk29_sdmmc_test_mrq_status(host, MRQ_STOP_START_DONE))
                send_stop_cmd(host);
-       //clk_disable(host->clk);
+       if(mrq->cmd->opcode == 17 && (host->data_intsts & SDMMC_INT_SBE)){
+               rk29_sdmmc_write(host->regs, SDMMC_CMD, SDMMC_CMD_STOP | SDMMC_CMD_START); 
+               while (--tmo && rk29_sdmmc_read(host->regs, SDMMC_CMD) & SDMMC_CMD_START);
+       }
        if(rk29_sdmmc_read(host->regs, SDMMC_STATUS) & SDMMC_STAUTS_FIFO_FULL ){
                rk29_sdmmc_read(host->regs, SDMMC_DATA);
                rk29_sdmmc_read(host->regs, SDMMC_DATA);
        }
        if(mrq->data && mrq->data->error)
                rk29_sdmmc_reset_fifo(host);
-
-       rk29_sdmmc_write(host->regs, SDMMC_CLKENA, 0);
        
        host->mrq = NULL;
        host->state = STATE_IDLE;
        rk29_sdmmc_set_mrq_status(host, MRQ_REQUEST_DONE);
        mmc_request_done(host->mmc, mrq);
+
+       
+       spin_lock(&host->lock);
 }
 
 static int sdmmc_send_cmd(struct rk29_sdmmc *host, unsigned int cmd, int arg)
@@ -488,6 +501,8 @@ int rk29_sdmmc_set_clock(struct rk29_sdmmc *host)
        if(!host->ios_clock)
                return 0;
        div  = (((host->bus_hz + (host->bus_hz / 5)) / host->ios_clock)) >> 1;
+       if(div == 0)
+               div = 1;
        if(host->div == div)
                return 0;
        
@@ -694,6 +709,11 @@ static int rk29_sdmmc_start_request(struct rk29_sdmmc *host,struct mmc_request *
        }
        dev_dbg(host->dev,"start cmd:%d ARGR=0x%08x CMDR=0x%08x\n",
                                                cmd->opcode, cmd->arg, cmdflags);
+       
+       rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, ~SDMMC_INT_SDIO);
+       rk29_sdmmc_write(host->regs, SDMMC_INTMASK,
+               rk29_sdmmc_read(host->regs, SDMMC_INTMASK) | 
+               (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS));
        ret = rk29_sdmmc_start_command(host, cmd, cmdflags);
        if(ret < 0) {
                rk29_sdmmc_set_mrq_status(host, MRQ_CMD_START_TMO);
@@ -713,11 +733,12 @@ start_err:
 
 static void rk29_sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
+       int timeout;
        struct rk29_sdmmc *host = mmc_priv(mmc);
 
        if(host->mrq) 
                rk29_sdmmc_show_info(host);
-       BUG_ON(host->mrq);
+       WARN_ON(host->mrq);
        host->old_mrq_status = host->mrq_status;
        host->mrq_status = 0;
        host->pending_events = 0;
@@ -725,15 +746,12 @@ static void rk29_sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
        host->cmd_intsts = 0;
        host->data_intsts = 0;
        host->mrq = mrq;
-       mod_timer(&host->monitor_timer, jiffies + msecs_to_jiffies(5000));
 
-       //clk_enable(host->clk);
-    rk29_sdmmc_write(host->regs, SDMMC_CLKENA, 1);
-       
-       rk29_sdmmc_write(host->regs, SDMMC_RINTSTS, ~SDMMC_INT_SDIO);
-       rk29_sdmmc_write(host->regs, SDMMC_INTMASK,
-               rk29_sdmmc_read(host->regs, SDMMC_INTMASK) | 
-               (SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS));
+       if(!mrq->data)
+               timeout = 500;
+       else
+               timeout = 500 + mrq->data->timeout_ns/1000000;
+       mod_timer(&host->monitor_timer, jiffies + msecs_to_jiffies(timeout));
        
        if (!rk29_sdmmc_get_cd(mmc)) {
                mrq->cmd->error = -ENOMEDIUM;
@@ -800,11 +818,8 @@ static void rk29_sdmmc_request_end(struct rk29_sdmmc *host)
        __releases(&host->lock)
        __acquires(&host->lock)
 {
-       spin_unlock(&host->lock);
        if(host->mrq)
                rk29_sdmmc_request_done(host, host->mrq);
-
-       spin_lock(&host->lock);
 }
 static void rk29_sdmmc_dma_cleanup(struct rk29_sdmmc *host)
 {
@@ -821,8 +836,8 @@ static void rk29_sdmmc_stop_dma(struct rk29_sdmmc *host)
        rk29_sdmmc_dma_cleanup(host);
        rk29_dma_ctrl(host->dma_info.chn,RK29_DMAOP_STOP);
        rk29_dma_ctrl(host->dma_info.chn,RK29_DMAOP_FLUSH);
-       //rk29_sdmmc_write(host->regs, SDMMC_CTRL, 
-                       //(rk29_sdmmc_read(host->regs, SDMMC_CTRL))&(~SDMMC_CTRL_DMA_ENABLE));
+       rk29_sdmmc_write(host->regs, SDMMC_CTRL, 
+                       (rk29_sdmmc_read(host->regs, SDMMC_CTRL))&(~SDMMC_CTRL_DMA_ENABLE));
 }
 
 static void rk29_sdmmc_command_complete(struct rk29_sdmmc *host,
@@ -1016,7 +1031,7 @@ static irqreturn_t rk29_sdmmc_isr(int irq, void *dev_id)
        }
 
        if(intsts & SDMMC_INT_DTO) {
-               rk29_sdmmc_set_mrq_status(host, MRQ_INT_DATE_DONE);
+               rk29_sdmmc_set_mrq_status(host, MRQ_INT_DATA_DONE);
            rk29_sdmmc_write(host->regs, SDMMC_RINTSTS,SDMMC_INT_DTO); 
            if (!host->data_intsts)
                        host->data_intsts = intsts;
@@ -1088,7 +1103,7 @@ static void rk29_sdmmc_detect_change(struct rk29_sdmmc *host)
                }
                rk29_sdmmc_request_end(host);
        }
-       rk29_sdmmc_reset_fifo(host);
+       //rk29_sdmmc_reset_ctrl(host);
        spin_unlock(&host->lock);
        mmc_detect_change(host->mmc, 0);
 }
@@ -1115,9 +1130,11 @@ static irqreturn_t rk29_sdmmc_detect_change_isr(int irq, void *dev_id);
 static void rk29_sdmmc_detect_change_work(struct work_struct *work)
 {
        int ret;
-    struct rk29_sdmmc *host =  container_of(work, struct rk29_sdmmc, work);
-       
-       if(host->enable_sd_warkup && rk29_sdmmc_get_cd(host->mmc) && !host->mmc->card)
+    struct rk29_sdmmc *host =  container_of(work, struct rk29_sdmmc, work.work);
+
+       dev_info(host->dev, "sd detect change, card is %s\n", 
+               rk29_sdmmc_get_cd(host->mmc)?"inserted":"removed");
+       if(host->enable_sd_warkup && rk29_sdmmc_get_cd(host->mmc))
                rk28_send_wakeup_key();
        rk29_sdmmc_detect_change(host);
 
@@ -1135,9 +1152,10 @@ static irqreturn_t rk29_sdmmc_detect_change_isr(int irq, void *dev_id)
        struct rk29_sdmmc *host = dev_id;
 
        disable_irq_nosync(host->gpio_irq);
-       dev_info(host->dev, "sd detect change, card is %s\n", 
-               rk29_sdmmc_get_cd(host->mmc)?"inserted":"removed");
-       schedule_work(&host->work);
+       if(rk29_sdmmc_get_cd(host->mmc))
+               schedule_delayed_work(&host->work, msecs_to_jiffies(500));
+       else
+               schedule_delayed_work(&host->work, 0);
        
        return IRQ_HANDLED;
 }
@@ -1146,14 +1164,38 @@ static void rk29_sdmmc_monitor_timer(unsigned long data)
        struct rk29_sdmmc *host = (struct rk29_sdmmc *)data;
        
        if(!rk29_sdmmc_test_mrq_status(host, MRQ_REQUEST_DONE)){
+               dev_info(host->dev, "no dto interrupt\n");
                rk29_sdmmc_show_info(host);
-               host->mrq->cmd->error = -ENOMEDIUM;
+               host->mrq->cmd->error = -ETIMEDOUT;
                if(host->mrq->data)
-                       host->mrq->data->error = -ENOMEDIUM;
+                       host->mrq->data->error = -ETIMEDOUT;
                rk29_sdmmc_request_end(host);
+               //rk29_sdmmc_reset_ctrl(host);
        }
        
 }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rk29_sdmmc_early_suspend(struct early_suspend *h)
+{
+       struct rk29_sdmmc *host = container_of(h,
+                                                       struct rk29_sdmmc,
+                                                       early_suspend);
+
+       dev_dbg(host->dev, "Enter rk29_sdmmc_early_suspend\n");
+}
+
+static void rk29_sdmmc_early_resume(struct early_suspend *h)
+{
+       struct rk29_sdmmc *host = container_of(h,
+                                                       struct rk29_sdmmc,
+                                                       early_suspend);
+
+       dev_dbg(host->dev, "Exit rk29_sdmmc_early_resume\n");
+}
+#endif
+
+
 static int rk29_sdmmc_probe(struct platform_device *pdev)
 {
        struct mmc_host                 *mmc;
@@ -1292,7 +1334,7 @@ static int rk29_sdmmc_probe(struct platform_device *pdev)
        
        host->gpio_det = pdata->detect_irq;
        if(!host->is_sdio && host->gpio_det != INVALID_GPIO) {
-               INIT_WORK(&host->work, rk29_sdmmc_detect_change_work);
+               INIT_DELAYED_WORK(&host->work, rk29_sdmmc_detect_change_work);
                ret = gpio_request(host->gpio_det, "sd_detect");
                if(ret < 0) {
                        dev_err(&pdev->dev, "gpio_request error\n");
@@ -1320,7 +1362,12 @@ static int rk29_sdmmc_probe(struct platform_device *pdev)
        rk29_sdmmc_write(host->regs, SDMMC_INTMASK,SDMMC_INT_CMD_DONE | SDMMC_INT_DTO | RK29_SDMMC_ERROR_FLAGS);
        rk29_sdmmc_write(host->regs, SDMMC_CTRL,SDMMC_CTRL_INT_ENABLE);
        rk29_sdmmc_write(host->regs, SDMMC_CLKENA,1);
-
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       host->early_suspend.suspend = rk29_sdmmc_early_suspend;
+       host->early_suspend.resume = rk29_sdmmc_early_resume;
+       host->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+       register_early_suspend(&host->early_suspend);
+#endif 
        dev_info(host->dev, "RK29 SDMMC controller at irq %d\n", host->irq);
        return 0;
        free_irq(host->gpio_irq, host);
@@ -1374,7 +1421,7 @@ static int rk29_sdmmc_suspend(struct platform_device *pdev, pm_message_t state)
 
        dev_info(host->dev, "Enter rk29_sdmmc_suspend\n");
        if(host->mmc && !host->is_sdio && host->gpio_det != INVALID_GPIO){
-               ret = mmc_suspend_host(host->mmc, state);
+               ret = mmc_suspend_host(host->mmc,state);
                if(!host->enable_sd_warkup)
                        free_irq(host->gpio_irq, host);
        }
@@ -1394,16 +1441,18 @@ static int rk29_sdmmc_resume(struct platform_device *pdev)
        clk_enable(host->clk);
     rk29_sdmmc_write(host->regs, SDMMC_CLKENA, 1);
        if(host->mmc && !host->is_sdio && host->gpio_det != INVALID_GPIO){
-               if(!host->enable_sd_warkup)
+               if(!host->enable_sd_warkup){
                        ret = request_irq(host->gpio_irq,
                                  rk29_sdmmc_detect_change_isr,
                                  rk29_sdmmc_get_cd(host->mmc)?IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING,
                                  "sd_detect",
                                  host);
-               if(ret < 0)
-               dev_err(host->dev, "gpio request_irq error\n");
-               mmc_detect_change(host->mmc, 0);
+                       if(ret < 0)
+                       dev_err(host->dev, "gpio request_irq error\n");
+               }
+               host->is_init = 1;
                ret = mmc_resume_host(host->mmc);
+               mmc_detect_change(host->mmc, 0);
        }
 #endif
        return ret;