X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fmmc%2Fhost%2Frk_sdmmc.c;h=5d591d049f830e46635ddb5c5f1c4ee16d81bd81;hb=bcec492cc1f6a3c6cbe8aa6e73b7b9d71acf6e55;hp=1f866f0ba781ae96f8d5507e4923497e8d29bb3d;hpb=55bf73c13b2c36ed7a33c1c66564fff46b53342f;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/mmc/host/rk_sdmmc.c b/drivers/mmc/host/rk_sdmmc.c index 1f866f0ba781..5d591d049f83 100755 --- a/drivers/mmc/host/rk_sdmmc.c +++ b/drivers/mmc/host/rk_sdmmc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -40,12 +42,15 @@ #include #include #include +#include +#include #include "rk_sdmmc.h" -#include "rk_sdmmc_of.h" +#include "rk_sdmmc_dbg.h" #include +#include "../../clk/rockchip/clk-ops.h" -#define RK_SDMMC_DRIVER_VERSION "Ver 1.00. The last modify date is 2014-05-05" +#define RK_SDMMC_DRIVER_VERSION "Ver 1.12 2014-07-08" /* Common flag combinations */ #define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ @@ -62,12 +67,13 @@ #define DW_MCI_FREQ_MAX 50000000//200000000 /* unit: HZ */ #define DW_MCI_FREQ_MIN 300000//400000 /* unit: HZ */ +/*max is 250ms showed in Spec; Maybe adapt the value for the sick card.*/ +#define SDMMC_DATA_TIMEOUT_SD 500 #define SDMMC_DATA_TIMEOUT_SDIO 250 -#define SDMMC_DATA_TIMEOUT_SD 5000; /*max is 250ms refer to Spec; Maybe adapt the value to the sick card.*/ #define SDMMC_DATA_TIMEOUT_EMMC 2500 -#define SDMMC_CMD_RTO_MAX_HOLD 200 -//#define SDMMC_WAIT_FOR_UNBUSY 2500 +#define SDMMC_CMD_RTO_MAX_HOLD 200 +#define SDMMC_WAIT_FOR_UNBUSY 2500 #ifdef CONFIG_MMC_DW_IDMAC #define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ @@ -131,6 +137,7 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); static void dw_mci_disable_low_power(struct dw_mci_slot *slot); /*printk the all register of current host*/ +#if 0 static int dw_mci_regs_printk(struct dw_mci *host) { struct sdmmc_reg *regs = dw_mci_regs; @@ -142,7 +149,7 @@ static int dw_mci_regs_printk(struct dw_mci *host) printk("=======printk %s-register end =========\n", mmc_hostname(host->mmc)); return 0; } - +#endif #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -311,6 +318,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) return cmdr; } +#if 0 static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) { struct mmc_command *stop; @@ -344,50 +352,39 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) return cmdr; } - +#endif static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { - unsigned long time_loop = jiffies + msecs_to_jiffies(5000); - unsigned int status; - bool ret=0; - - host->pre_cmd = host->cmd; + struct dw_mci_slot *slot = host->slot[0]; + /*temporality fix slot[0] due to host->num_slots equal to 1*/ + + host->pre_cmd = host->cmd; host->cmd = cmd; dev_vdbg(host->dev, - "start command: ARGR=0x%08x CMDR=0x%08x\n", - cmd->arg, cmd_flags); - - if(SD_SWITCH_VOLTAGE == cmd->opcode){ - /*confirm non-low-power mode*/ - struct dw_mci_slot *slot = host->slot[0];//temporality fix slot[0] due to host->num_slots equal to 1; - mci_writel(host, CMDARG, 0); - dw_mci_disable_low_power(slot); - MMC_DBG_INFO_FUNC(host->mmc,"Line%d..%s before start cmd=11,[%s]", - __LINE__, __FUNCTION__,mmc_hostname(host->mmc)); - - cmd_flags |= SDMMC_CMD_VOLT_SWITCH; - } + "start command: ARGR=0x%08x CMDR=0x%08x\n", + cmd->arg, cmd_flags); + + if(SD_SWITCH_VOLTAGE == cmd->opcode){ + /*confirm non-low-power mode*/ + mci_writel(host, CMDARG, 0); + dw_mci_disable_low_power(slot); + + MMC_DBG_INFO_FUNC(host->mmc,"Line%d..%s before start cmd=11,[%s]", + __LINE__, __FUNCTION__,mmc_hostname(host->mmc)); + + cmd_flags |= SDMMC_CMD_VOLT_SWITCH; + } mci_writel(host, CMDARG, cmd->arg); - if(host->mmc->hold_reg_flag) - cmd_flags |= SDMMC_CMD_USE_HOLD_REG;//fix the value to 1 in some Soc,for example RK3188. - - mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); - wmb(); + wmb(); + + /* fix the value to 1 in some Soc,for example RK3188. */ + if(host->mmc->hold_reg_flag) + cmd_flags |= SDMMC_CMD_USE_HOLD_REG; - while ((time_before(jiffies, time_loop))&&(test_bit(DW_MMC_CARD_PRESENT, &host->cur_slot->flags))){ - status = mci_readl(host, STATUS); - if (!(status & (SDMMC_STAUTS_DATA_BUSY|SDMMC_STAUTS_MC_BUSY))){ - ret = 1 ;//card is unbusy - break; - } - } ; - if(!ret) - MMC_DBG_ERR_FUNC(host->mmc,"Line%d..%s start cmd=%d(arg=0x%x), cmd_reg=0x%x, unbusy=%d,card-present=%d. [%s]", - __LINE__, __FUNCTION__,cmd->opcode, cmd->arg,cmd_flags, - ret,test_bit(DW_MMC_CARD_PRESENT, &host->cur_slot->flags), mmc_hostname(host->mmc)); - + mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); + wmb(); } static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) @@ -453,27 +450,29 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host) mci_writel(host, BMOD, temp); } -static void dw_mci_idmac_complete_dma(struct dw_mci *host) +static void dw_mci_idmac_complete_dma(void *arg) { - struct mmc_data *data = host->data; + struct dw_mci *host = arg; + struct mmc_data *data = host->data; - dev_vdbg(host->dev, "DMA complete\n"); + dev_vdbg(host->dev, "DMA complete\n"); - /* - MMC_DBG_CMD_FUNC(host->mmc," DMA complete cmd=%d(arg=0x%x), blocks=%d,blksz=%d[%s]", \ - host->mrq->cmd->opcode,host->mrq->cmd->arg,data->blocks,data->blksz,mmc_hostname(host->mmc)); - */ - - host->dma_ops->cleanup(host); + /* + MMC_DBG_CMD_FUNC(host->mmc," DMA complete cmd=%d(arg=0x%x), blocks=%d,blksz=%d[%s]", \ + host->mrq->cmd->opcode,host->mrq->cmd->arg, + data->blocks,data->blksz,mmc_hostname(host->mmc)); + */ + + host->dma_ops->cleanup(host); /* * If the card was removed, data will be NULL. No point in trying to * send the stop command or waiting for NBUSY in this case. */ - if (data) { - set_bit(EVENT_XFER_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); - } + if(data){ + set_bit(EVENT_XFER_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet); + } } static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, @@ -565,6 +564,145 @@ static const struct dw_mci_dma_ops dw_mci_idmac_ops = { .complete = dw_mci_idmac_complete_dma, .cleanup = dw_mci_dma_cleanup, }; + + +static void dw_mci_edma_cleanup(struct dw_mci *host) +{ + struct mmc_data *data = host->data; + + if (data) + if (!data->host_cookie) + dma_unmap_sg(host->dev, + data->sg, data->sg_len, + dw_mci_get_dma_dir(data)); +} + +static void dw_mci_edmac_stop_dma(struct dw_mci *host) +{ + dmaengine_terminate_all(host->dms->ch); +} + +static void dw_mci_edmac_complete_dma(void *arg) +{ + struct dw_mci *host = arg; + struct mmc_data *data = host->data; + + dev_vdbg(host->dev, "DMA complete\n"); + + if(data) + if(data->flags & MMC_DATA_READ) + /* Invalidate cache after read */ + dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, + data->sg_len, DMA_FROM_DEVICE); + + host->dma_ops->cleanup(host); + + /* + * If the card was removed, data will be NULL. No point in trying to + * send the stop command or waiting for NBUSY in this case. + */ + if (data) { + set_bit(EVENT_XFER_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet); + } +} + +static void dw_mci_edmac_start_dma(struct dw_mci *host, unsigned int sg_len) +{ + struct dma_slave_config slave_config; + struct dma_async_tx_descriptor *desc = NULL; + struct scatterlist *sgl = host->data->sg; + u32 sg_elems = host->data->sg_len; + int ret = 0; + + /* Set external dma config: burst size, burst width*/ + slave_config.dst_addr = (dma_addr_t)(host->phy_regs + host->data_offset); + slave_config.src_addr = slave_config.dst_addr; + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config.src_addr_width = slave_config.dst_addr_width; + + /* Match FIFO dma burst MSIZE with external dma config*/ + slave_config.dst_maxburst = ((host->fifoth_val) >> 28) && 0x7; + slave_config.src_maxburst = slave_config.dst_maxburst; + + if(host->data->flags & MMC_DATA_WRITE){ + slave_config.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(host->dms->ch, &slave_config); + if(ret){ + dev_err(host->dev, "error in dw_mci edma configuration.\n"); + return; + } + + desc = dmaengine_prep_slave_sg(host->dms->ch, sgl, sg_len, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if(!desc){ + dev_err(host->dev, "We cannot prepare for the dw_mci slave edma!\n"); + return; + } + /* Set dw_mci_edmac_complete_dma as callback */ + desc->callback = dw_mci_edmac_complete_dma; + desc->callback_param = (void *)host; + dmaengine_submit(desc); + + /* Flush cache before write */ + dma_sync_sg_for_device(mmc_dev(host->mmc), sgl, + sg_elems, DMA_TO_DEVICE); + dma_async_issue_pending(host->dms->ch); + }else{ + /* MMC_DATA_READ*/ + slave_config.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(host->dms->ch, &slave_config); + if(ret){ + dev_err(host->dev, "error in dw_mci edma configuration.\n"); + return; + } + desc = dmaengine_prep_slave_sg(host->dms->ch, sgl, sg_len, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if(!desc){ + dev_err(host->dev, "We cannot prepare for the dw_mci slave edma!\n"); + return; + } + /* set dw_mci_edmac_complete_dma as callback */ + desc->callback = dw_mci_edmac_complete_dma; + desc->callback_param = (void *)host; + dmaengine_submit(desc); + dma_async_issue_pending(host->dms->ch); + } +} + +static int dw_mci_edmac_init(struct dw_mci *host) +{ + /* 1) request external dma channel, SHOULD decide chn in dts */ + host->dms = (struct dw_mci_dma_slave *)kmalloc(sizeof(struct dw_mci_dma_slave),GFP_KERNEL); + host->dms->ch = dma_request_slave_channel(host->dev, "dw_mci"); + if (!host->dms->ch){ + dev_err(host->dev, "Failed to get external DMA channel: channel id = %d\n", + host->dms->ch->chan_id); + goto err_exit; + } + + /* anything? */ + + return 0; + +err_exit: + return -ENODEV; + +} + +static void dw_mci_edmac_exit(struct dw_mci *host) +{ + dma_release_channel(host->dms->ch); +} + +static const struct dw_mci_dma_ops dw_mci_edmac_ops = { + .init = dw_mci_edmac_init, + .exit = dw_mci_edmac_exit, + .start = dw_mci_edmac_start_dma, + .stop = dw_mci_edmac_stop_dma, + .complete = dw_mci_edmac_complete_dma, + .cleanup = dw_mci_edma_cleanup, +}; #endif /* CONFIG_MMC_DW_IDMAC */ static int dw_mci_pre_dma_transfer(struct dw_mci *host, @@ -829,18 +967,20 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) { struct dw_mci *host = slot->host; - unsigned long timeout = jiffies + msecs_to_jiffies(5000); + unsigned long timeout = jiffies + msecs_to_jiffies(500);//msecs_to_jiffies(5000); unsigned int cmd_status = 0; #ifdef SDMMC_WAIT_FOR_UNBUSY + bool ret = true; timeout = jiffies + msecs_to_jiffies(SDMMC_WAIT_FOR_UNBUSY); - bool ret=0; + if(test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { - while (ret=time_before(jiffies, timeout)) { + while (ret) { + ret = time_before(jiffies, timeout); cmd_status = mci_readl(host, STATUS); if (!(cmd_status & (SDMMC_STAUTS_DATA_BUSY|SDMMC_STAUTS_MC_BUSY))) break; } ; - if(!ret) + if(false == ret) printk("%d..%s: wait for unbusy timeout.......[%s]\n", \ __LINE__, __FUNCTION__, mmc_hostname(host->mmc)); } @@ -848,11 +988,14 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) mci_writel(host, CMDARG, arg); wmb(); mci_writel(host, CMD, SDMMC_CMD_START | cmd); - + if(cmd & SDMMC_CMD_UPD_CLK) + timeout = jiffies + msecs_to_jiffies(50); + else + timeout = jiffies + msecs_to_jiffies(500); while (time_before(jiffies, timeout)) { cmd_status = mci_readl(host, CMD); if (!(cmd_status & SDMMC_CMD_START)) - return; + return ; } dev_err(&slot->mmc->class_dev, "Timeout sending command (cmd %#x arg %#x status %#x)\n", @@ -866,14 +1009,18 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) u32 div; u32 clk_en_a; u32 sdio_int; - - MMC_DBG_INFO_FUNC(host->mmc,"%d..%s: clock=%d, current_speed=%d, bus_hz=%d,forc=%d[%s]\n", - __LINE__, __FUNCTION__, clock, host->current_speed,host->bus_hz,force_clkinit,mmc_hostname(host->mmc)); + + MMC_DBG_INFO_FUNC(host->mmc,"%d..%s: clock=%d, current_speed=%d, bus_hz=%d,forc=%d[%s]\n", + __LINE__, __FUNCTION__, clock, host->current_speed,host->bus_hz,force_clkinit,mmc_hostname(host->mmc)); if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + #ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE + if(host->svi_flags == 0) + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + #else + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + #endif } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -893,7 +1040,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) tempck, div); host->set_speed = tempck; - host->set_div = div; + host->set_div = div; } /* disable clock */ @@ -903,7 +1050,61 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* inform CIU */ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); - + + if(clock <= 400*1000){ + MMC_DBG_BOOT_FUNC(host->mmc, + "dw_mci_setup_bus: argue clk_mmc workaround out %dHz for init[%s]", + clock * 2, mmc_hostname(host->mmc)); + /* clk_mmc will change parents to 24MHz xtal*/ + clk_set_rate(host->clk_mmc, clock * 2); + + div = 0; + host->set_div = div; + } + else + { + MMC_DBG_BOOT_FUNC(host->mmc, + "dw_mci_setup_bus: argue clk_mmc workaround out normal clock [%s]", + mmc_hostname(host->mmc)); + if(div > 1) + { + MMC_DBG_ERR_FUNC(host->mmc, + "dw_mci_setup_bus: div SHOULD NOT LARGER THAN ONE! [%s]", + mmc_hostname(host->mmc)); + div = 1; + host->set_div = div; + host->bus_hz = host->set_speed * 2; + MMC_DBG_BOOT_FUNC(host->mmc, + "dw_mci_setup_bus: workaround div = %d, host->bus_hz = %d [%s]", + div, host->bus_hz, mmc_hostname(host->mmc)); + } + /* BUG may be here, come on, Linux BSP engineer looks! + FIXME: HS-DDR eMMC, div SHOULD be ONE, but we here cannot fetch eMMC bus mode!!!!!!!! + WRONG dts set clk = 50M, and calc div be zero. Controller denied this setting! + some oops happened like that: + mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 50000000Hz, actual 50000000HZ div = 0) + rk_sdmmc: BOOT dw_mci_setup_bus: argue clk_mmc workaround out normal clock [mmc0] + rk_sdmmc: BOOT Bus speed=50000000Hz,Bus width=8bits.[mmc0] + mmc0: new high speed DDR MMC card at address 0001 + mmcblk0: mmc0:0001 M8G1GC 7.28 GiB + .... + mmcblk0: error -84 transferring data, sector 606208, nr 32, cmd response 0x900, card status 0xb00 + mmcblk0: retrying using single block read + mmcblk0: error -110 sending status command, retrying + + How to: If eMMC HW version < 4.51, or > 4.51 but no caps2-mmc-hs200 support in dts + Please set dts emmc clk to 100M or 150M, I will workaround it! + */ + + if (host->verid < DW_MMC_240A) + clk_set_rate(host->clk_mmc,(host->bus_hz)); + else + clk_set_rate(host->clk_mmc,(host->bus_hz) * 2); + + + + } + /* set clock to desired speed */ mci_writel(host, CLKDIV, div); @@ -916,7 +1117,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (host->verid < DW_MMC_240A) sdio_int = SDMMC_INT_SDIO(slot->id); - else + else sdio_int = SDMMC_INT_SDIO((slot->id) + 8); if (!(mci_readl(host, INTMASK) & sdio_int)) @@ -926,7 +1127,6 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* inform CIU */ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); - /* keep the clock with reflecting clock dividor */ slot->__clk_old = clock << div; } @@ -942,46 +1142,77 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* Set the current slot bus width */ mci_writel(host, CTYPE, (slot->ctype << slot->id)); +} + +extern struct mmc_card *this_card; +static void dw_mci_wait_unbusy(struct dw_mci *host) +{ + + unsigned int timeout= SDMMC_DATA_TIMEOUT_SDIO; + unsigned long time_loop; + unsigned int status; + u32 se_flag = 0; + + MMC_DBG_INFO_FUNC(host->mmc, "dw_mci_wait_unbusy, status=0x%x ", mci_readl(host, STATUS)); + + if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC) { + if (host->cmd && (host->cmd->opcode == MMC_ERASE)) { + /* Special care for (secure)erase timeout calculation */ + if(this_card){ + if((host->cmd->arg & (0x1 << 31)) == 1) /* secure erase */ + se_flag = 0x1; + + if (((this_card->ext_csd.erase_group_def) & 0x1) == 1) ; + se_flag ? (timeout = (this_card->ext_csd.hc_erase_timeout) * + 300000 * (this_card->ext_csd.sec_erase_mult)) : + (timeout = (this_card->ext_csd.hc_erase_timeout) * 300000); + } + } + + if(timeout < SDMMC_DATA_TIMEOUT_EMMC) + timeout = SDMMC_DATA_TIMEOUT_EMMC; + } else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) { + timeout = SDMMC_DATA_TIMEOUT_SD; + } + time_loop = jiffies + msecs_to_jiffies(timeout); + do { + status = mci_readl(host, STATUS); + if (!(status & (SDMMC_STAUTS_DATA_BUSY | SDMMC_STAUTS_MC_BUSY))) + break; + } while (time_before(jiffies, time_loop)); } + + +#ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE /* * result: * 0--status is busy. * 1--status is unbusy. */ -int dw_mci_wait_unbusy(struct mmc_host *mmc) +int dw_mci_card_busy(struct mmc_host *mmc) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; - unsigned int timeout= SDMMC_DATA_TIMEOUT_SDIO; - unsigned long time_loop; - unsigned int status; - int ret= 0; + + MMC_DBG_INFO_FUNC(host->mmc, "dw_mci_card_busy: svi_flags = %d [%s]", \ + host->svi_flags, mmc_hostname(host->mmc)); - if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC) - timeout = SDMMC_DATA_TIMEOUT_EMMC; - else if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) - timeout = SDMMC_DATA_TIMEOUT_SD; - timeout = 250*1000;//test - time_loop = jiffies + msecs_to_jiffies(timeout); + /* svi toggle*/ + if(host->svi_flags == 0){ + /*first svi*/ + host->svi_flags = 1; + return host->svi_flags; - MMC_DBG_INFO_FUNC(host->mmc, "line%d: dw_mci_wait_unbusy,timeloop=%lu, status=0x%x ", - __LINE__, time_loop, mci_readl(host, STATUS)); - do { - status = mci_readl(host, STATUS); - if (!(status & (SDMMC_STAUTS_DATA_BUSY|SDMMC_STAUTS_MC_BUSY))){ - ret = 1;//card is unbusy. - break; + }else{ + host->svi_flags = 0; + return host->svi_flags; } - //MMC_DBG_INFO_FUNC("dw_mci_wait_unbusy, waiting for......"); - } while (time_before(jiffies, time_loop)); - MMC_DBG_INFO_FUNC(host->mmc, "line%d: dw_mci_wait_unbusy,ret=%d, status=0x%x ", - __LINE__,ret,mci_readl(host, STATUS)); + - return ret; } - +#endif static void __dw_mci_start_request(struct dw_mci *host, struct dw_mci_slot *slot, struct mmc_command *cmd) @@ -996,15 +1227,8 @@ static void __dw_mci_start_request(struct dw_mci *host, host->cur_slot = slot; host->mrq = mrq; -#if 0 //add by xbw,at 2014-03-12 - /*clean FIFO if it is a new request*/ - if(!(mrq->cmd->opcode & SDMMC_CMD_STOP)) { - MMC_DBG_INFO_FUNC("%d..%s: reset the ctrl.", __LINE__, __FUNCTION__); - mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET)); - } - #endif - dw_mci_wait_unbusy(host->mmc); + + dw_mci_wait_unbusy(host); host->pending_events = 0; host->completed_events = 0; @@ -1039,9 +1263,9 @@ static void dw_mci_start_request(struct dw_mci *host, { struct mmc_request *mrq = slot->mrq; struct mmc_command *cmd; - - MMC_DBG_INFO_FUNC(host->mmc, " Begin to start the new request. cmd=%d(arg=0x%x)[%s]", \ - mrq->cmd->opcode, mrq->cmd->arg, mmc_hostname(host->mmc)); + + MMC_DBG_INFO_FUNC(host->mmc, " Begin to start the new request. cmd=%d(arg=0x%x)[%s]", \ + mrq->cmd->opcode, mrq->cmd->arg, mmc_hostname(host->mmc)); cmd = mrq->sbc ? mrq->sbc : mrq->cmd; __dw_mci_start_request(host, slot, cmd); @@ -1087,8 +1311,9 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) mmc_request_done(mmc, mrq); return; } - MMC_DBG_CMD_FUNC(host->mmc, "======>\n pull a new request from MMC-frame to dw_mci_queue. cmd=%d(arg=0x%x)[%s]", \ - mrq->cmd->opcode, mrq->cmd->arg, mmc_hostname(host->mmc)); + + MMC_DBG_CMD_FUNC(host->mmc, "======>\n pull a new request from MMC-frame to dw_mci_queue. cmd=%d(arg=0x%x)[%s]", \ + mrq->cmd->opcode, mrq->cmd->arg, mmc_hostname(host->mmc)); dw_mci_queue_request(host, slot, mrq); @@ -1098,31 +1323,50 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct dw_mci_slot *slot = mmc_priv(mmc); + #ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE + struct dw_mci *host = slot->host; + #endif const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 regs; -#ifdef SDMMC_WAIT_FOR_UNBUSY - unsigned long time_loop = jiffies + msecs_to_jiffies(SDMMC_WAIT_FOR_UNBUSY); - bool ret=0; - - if(!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)){ - printk("%d..%s: no card. [%s]\n", \ - __LINE__, __FUNCTION__, mmc_hostname(mmc)); - goto EXIT_POWER; - } + + #ifdef SDMMC_WAIT_FOR_UNBUSY + unsigned long time_loop; + bool ret = true; + + #ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE + if(host->svi_flags == 1) + time_loop = jiffies + msecs_to_jiffies(SDMMC_DATA_TIMEOUT_SD); + else + time_loop = jiffies + msecs_to_jiffies(SDMMC_WAIT_FOR_UNBUSY); + #else + time_loop = jiffies + msecs_to_jiffies(SDMMC_WAIT_FOR_UNBUSY); + #endif + + if(!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)){ + printk("%d..%s: no card. [%s]\n", \ + __LINE__, __FUNCTION__, mmc_hostname(mmc)); + goto EXIT_POWER; + } - while (ret=time_before(jiffies, time_loop)) { - regs = mci_readl(slot->host, STATUS); - if (!(regs & (SDMMC_STAUTS_DATA_BUSY|SDMMC_STAUTS_MC_BUSY))) - break; - } ; - if(!ret) - { - printk("slot->flags=%d ", slot->flags); - dump_stack(); - printk("%d..%s: wait for unbusy timeout.......[%s]\n", \ - __LINE__, __FUNCTION__, mmc_hostname(mmc)); - } -#endif + while (ret) { + ret = time_before(jiffies, time_loop); + regs = mci_readl(slot->host, STATUS); + if (!(regs & (SDMMC_STAUTS_DATA_BUSY|SDMMC_STAUTS_MC_BUSY))) + break; + }; + + if(false == ret) + { + printk("slot->flags = %lu ", slot->flags); + #ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE + if(host->svi_flags != 1) + #endif + dump_stack(); + printk("%d..%s: wait for unbusy timeout....... STATUS = 0x%x [%s]\n", \ + __LINE__, __FUNCTION__, regs, mmc_hostname(mmc)); + } + #endif + switch (ios->bus_width) { case MMC_BUS_WIDTH_4: slot->ctype = SDMMC_CTYPE_4BIT; @@ -1158,28 +1402,29 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Slot specific timing and width adjustment */ dw_mci_setup_bus(slot, false); + //return -EAGAIN; EXIT_POWER: switch (ios->power_mode) { - case MMC_POWER_UP: - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - /* Power up slot */ - if (slot->host->pdata->setpower) - slot->host->pdata->setpower(slot->id, mmc->ocr_avail); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); - break; - case MMC_POWER_OFF: - /* Power down slot */ - if (slot->host->pdata->setpower) - slot->host->pdata->setpower(slot->id, 0); - regs = mci_readl(slot->host, PWREN); - regs &= ~(1 << slot->id); - mci_writel(slot->host, PWREN, regs); - break; - default: - break; + case MMC_POWER_UP: + set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + /* Power up slot */ + if (slot->host->pdata->setpower) + slot->host->pdata->setpower(slot->id, mmc->ocr_avail); + regs = mci_readl(slot->host, PWREN); + regs |= (1 << slot->id); + mci_writel(slot->host, PWREN, regs); + break; + case MMC_POWER_OFF: + /* Power down slot */ + if(slot->host->pdata->setpower) + slot->host->pdata->setpower(slot->id, 0); + regs = mci_readl(slot->host, PWREN); + regs &= ~(1 << slot->id); + mci_writel(slot->host, PWREN, regs); + break; + default: + break; } } @@ -1190,11 +1435,11 @@ static int dw_mci_get_ro(struct mmc_host *mmc) struct dw_mci_board *brd = slot->host->pdata; /* Use platform get_ro function, else try on board write protect */ - if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) + if(slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) read_only = 0; - else if (brd->get_ro) + else if(brd->get_ro) read_only = brd->get_ro(slot->id); - else if (gpio_is_valid(slot->wp_gpio)) + else if(gpio_is_valid(slot->wp_gpio)) read_only = gpio_get_value(slot->wp_gpio); else read_only = @@ -1208,37 +1453,71 @@ static int dw_mci_get_ro(struct mmc_host *mmc) static int dw_mci_set_sdio_status(struct mmc_host *mmc, int val) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_board *brd = slot->host->pdata; - struct dw_mci *host = slot->host; - if (!(mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO)) - return 0; - - spin_lock_bh(&host->lock); - if(val) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - else - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - - spin_unlock_bh(&host->lock); - - mmc_detect_change(slot->mmc, 20); + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + /*struct dw_mci_board *brd = slot->host->pdata;*/ - return 0; + if (!(mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO)) + return 0; + + spin_lock_bh(&host->lock); + + if(val) + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); + else + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); + + spin_unlock_bh(&host->lock); + + if(test_bit(DW_MMC_CARD_PRESENT, &slot->flags)){ + if(__clk_is_enabled(host->hclk_mmc) == false) + clk_prepare_enable(host->hclk_mmc); + if(__clk_is_enabled(host->clk_mmc) == false) + clk_prepare_enable(host->clk_mmc); + }else{ + if(__clk_is_enabled(host->clk_mmc) == true) + clk_disable_unprepare(slot->host->clk_mmc); + if(__clk_is_enabled(host->hclk_mmc) == true) + clk_disable_unprepare(slot->host->hclk_mmc); + } + + mmc_detect_change(slot->mmc, 20); + return 0; } static int dw_mci_get_cd(struct mmc_host *mmc) { - int present; - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_board *brd = slot->host->pdata; - struct dw_mci *host = slot->host; - int gpio_cd = mmc_gpio_get_cd(mmc); - - if (mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) - return test_bit(DW_MMC_CARD_PRESENT, &slot->flags); + int present; + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci_board *brd = slot->host->pdata; + struct dw_mci *host = slot->host; + int gpio_cd = mmc_gpio_get_cd(mmc); + int gpio_val; + + if (cpu_is_rk312x() && + soc_is_rk3126() && + (mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)) { + gpio_cd = slot->cd_gpio; + if (gpio_is_valid(gpio_cd)) { + gpio_val = gpio_get_value_cansleep(gpio_cd); + msleep(10); + if (gpio_val == gpio_get_value_cansleep(gpio_cd)) { + gpio_cd = gpio_get_value_cansleep(gpio_cd) == 0 ? 1 : 0; + if (gpio_cd == 0) + dw_mci_ctrl_all_reset(host); + } else { + /* Jitter */ + return slot->last_detect_state; + } + } else { + dev_err(host->dev, "dw_mci_get_cd: invalid gpio_cd!\n"); + } + } + + if (mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) + return test_bit(DW_MMC_CARD_PRESENT, &slot->flags); /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) @@ -1264,25 +1543,105 @@ static int dw_mci_get_cd(struct mmc_host *mmc) return present; } + +/* + * Dts Should caps emmc controller with poll-hw-reset + */ static void dw_mci_hw_reset(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); - - /* - * According to eMMC spec - * tRstW >= 1us ; RST_n pulse width - * tRSCA >= 200us ; RST_n to Command time - * tRSTH >= 1us ; RST_n high period - */ - - mci_writel(slot->host, RST_n, 0x1); - dsb(); - udelay(10); //10us for bad quality eMMc. - - mci_writel(slot->host, RST_n, 0x0); - dsb(); - usleep_range(300, 1000); //ay least 300(> 200us) - + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + u32 regs; + + #if 0 + u32 cmd_flags; + unsigned long timeout; + bool ret = true; + + /* (1) CMD12 to end any transfer in process */ + cmd_flags = SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC + | SDMMC_CMD_RESP_EXP | MMC_STOP_TRANSMISSION; + + if(host->mmc->hold_reg_flag) + cmd_flags |= SDMMC_CMD_USE_HOLD_REG; + mci_writel(host, CMDARG, 0); + wmb(); + mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); + wmb(); + timeout = jiffies + msecs_to_jiffies(500); + while(ret){ + ret = time_before(jiffies, timeout); + if(!(mci_readl(host, CMD) & SDMMC_CMD_START)) + break; + } + + if(false == ret) + MMC_DBG_ERR_FUNC(host->mmc, + "%s dw_mci_hw_reset: STOP_TRANSMISSION failed!!! [%s]\n", + __func__, mmc_hostname(host->mmc)); + + /* (2) wait DTO, even if no response is sent back by card */ + ret = true; + timeout = jiffies + msecs_to_jiffies(5); + while(ret){ + ret = time_before(jiffies, timeout); + if(!(mci_readl(host, MINTSTS) & SDMMC_INT_DATA_OVER)){ + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); + break; + } + } + #endif + + /* (3) Reset following: DONNOT CHANGE RESET ORDER!*/ + + /* Software reset - BMOD[0] for IDMA only */ + regs = mci_readl(host, BMOD); + regs |= SDMMC_IDMAC_SWRESET; + mci_writel(host, BMOD, regs); + udelay(1); /* Auto cleared after 1 cycle, 1us is enough for hclk_mmc */ + regs = mci_readl(host, BMOD); + if(regs & SDMMC_IDMAC_SWRESET) + MMC_DBG_WARN_FUNC(host->mmc, + "%s dw_mci_hw_reset: SDMMC_IDMAC_SWRESET failed!!! [%s]\n", + __func__, mmc_hostname(host->mmc)); + + /* DMA reset - CTRL[2] */ + regs = mci_readl(host, CTRL); + regs |= SDMMC_CTRL_DMA_RESET; + mci_writel(host, CTRL, regs); + udelay(1); /* Auto cleared after 2 AHB clocks, 1us is enough plus mci_readl access */ + regs = mci_readl(host, CTRL); + if(regs & SDMMC_CTRL_DMA_RESET) + MMC_DBG_WARN_FUNC(host->mmc, + "%s dw_mci_hw_reset: SDMMC_CTRL_DMA_RESET failed!!! [%s]\n", + __func__, mmc_hostname(host->mmc)); + + /* FIFO reset - CTRL[1] */ + regs = mci_readl(host, CTRL); + regs |= SDMMC_CTRL_FIFO_RESET; + mci_writel(host, CTRL, regs); + mdelay(1); /* no timing limited, 1ms is random value */ + regs = mci_readl(host, CTRL); + if(regs & SDMMC_CTRL_FIFO_RESET) + MMC_DBG_WARN_FUNC(host->mmc, + "%s dw_mci_hw_reset: SDMMC_CTRL_DMA_RESET failed!!! [%s]\n", + __func__, mmc_hostname(host->mmc)); + + /* (4) CARD_RESET + According to eMMC spec + tRstW >= 1us ; RST_n pulse width + tRSCA >= 200us ; RST_n to Command time + tRSTH >= 1us ; RST_n high period + */ + mci_writel(slot->host, PWREN, 0x0); + mci_writel(slot->host, RST_N, 0x0); + dsb(); + udelay(10); /* 10us for bad quality eMMc. */ + + mci_writel(slot->host, PWREN, 0x1); + mci_writel(slot->host, RST_N, 0x1); + dsb(); + usleep_range(500, 1000); /* at least 500(> 200us) */ } /* @@ -1319,7 +1678,7 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); - if (host->verid < DW_MMC_240A) + if (host->verid < DW_MMC_240A) sdio_int = SDMMC_INT_SDIO(slot->id); else sdio_int = SDMMC_INT_SDIO((slot->id) + 8); @@ -1341,6 +1700,41 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) } } +#ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE +enum{ + IO_DOMAIN_12 = 1200, + IO_DOMAIN_18 = 1800, + IO_DOMAIN_33 = 3300, +}; +static void dw_mci_do_grf_io_domain_switch(struct dw_mci *host, u32 voltage) +{ + switch(voltage){ + case IO_DOMAIN_33: + voltage = 0; + break; + case IO_DOMAIN_18: + voltage = 1; + break; + case IO_DOMAIN_12: + MMC_DBG_ERR_FUNC(host->mmc,"%s : Not support io domain voltage [%s]\n", + __FUNCTION__, mmc_hostname(host->mmc)); + break; + default: + MMC_DBG_ERR_FUNC(host->mmc,"%s : Err io domain voltage [%s]\n", + __FUNCTION__, mmc_hostname(host->mmc)); + break; + } + + if(cpu_is_rk3288()){ + if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) + grf_writel((voltage << 7) | (1 << 23), RK3288_GRF_IO_VSEL); + else + return ; + }else{ + MMC_DBG_ERR_FUNC(host->mmc,"%s : unknown chip [%s]\n", + __FUNCTION__, mmc_hostname(host->mmc)); + } +} static int dw_mci_do_start_signal_voltage_switch(struct dw_mci *host, struct mmc_ios *ios) @@ -1356,76 +1750,80 @@ static int dw_mci_do_start_signal_voltage_switch(struct dw_mci *host, return 0; uhs_reg = mci_readl(host, UHS_REG); - MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s: vol=%d.[%s]\n", - __LINE__, __FUNCTION__,ios->signal_voltage, mmc_hostname(host->mmc)); + MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s: vol=%d.[%s]\n", + __LINE__, __FUNCTION__,ios->signal_voltage, mmc_hostname(host->mmc)); switch (ios->signal_voltage) { case MMC_SIGNAL_VOLTAGE_330: /* Set 1.8V Signal Enable in the Host Control2 register to 0 */ if (host->vmmc) { ret = io_domain_regulator_set_voltage(host->vmmc, 3300000, 3300000); - //regulator_put(host->vmmc); //to be done in remove function. + /* regulator_put(host->vmmc); //to be done in remove function. */ MMC_DBG_SW_VOL_FUNC(host->mmc,"%s =%dmV set 3.3end, ret=%d \n", __func__, regulator_get_voltage(host->vmmc), ret); if (ret) { - pr_warning("%s: Switching to 3.3V signalling voltage " + MMC_DBG_SW_VOL_FUNC(host->mmc, "%s: Switching to 3.3V signalling voltage " " failed\n", mmc_hostname(host->mmc)); return -EIO; } + dw_mci_do_grf_io_domain_switch(host, IO_DOMAIN_33); } - MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s: [%s]\n",__LINE__, __FUNCTION__, mmc_hostname(host->mmc)); + MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s: [%s]\n",__LINE__, + __FUNCTION__, mmc_hostname(host->mmc)); - //set High-power mode + /* set High-power mode */ value = mci_readl(host, CLKENA); - mci_writel(host,CLKENA , value& ~SDMMC_CLKEN_LOW_PWR); - //SDMMC_UHS_REG - mci_writel(host,UHS_REG , uhs_reg & ~SDMMC_UHS_VOLT_REG_18); + value &= ~SDMMC_CLKEN_LOW_PWR; + mci_writel(host,CLKENA , value); + /* SDMMC_UHS_REG */ + uhs_reg &= ~SDMMC_UHS_VOLT_REG_18; + mci_writel(host,UHS_REG , uhs_reg); /* Wait for 5ms */ usleep_range(5000, 5500); /* 3.3V regulator output should be stable within 5 ms */ uhs_reg = mci_readl(host, UHS_REG); - if( !(uhs_reg & SDMMC_UHS_VOLT_REG_18)) - return 0; + if( !(uhs_reg & SDMMC_UHS_VOLT_REG_18)) + return 0; - pr_warning("%s: 3.3V regulator output did not became stable\n", + MMC_DBG_SW_VOL_FUNC(host->mmc, "%s: 3.3V regulator output did not became stable\n", mmc_hostname(host->mmc)); return -EAGAIN; case MMC_SIGNAL_VOLTAGE_180: if (host->vmmc) { ret = io_domain_regulator_set_voltage(host->vmmc,1800000, 1800000); -// regulator_put(host->vmmc);//to be done in remove function. + /* regulator_put(host->vmmc);//to be done in remove function. */ MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s =%dmV set 1.8end, ret=%d . \n", __LINE__, __func__, regulator_get_voltage(host->vmmc), ret); if (ret) { - pr_warning("%s: Switching to 1.8V signalling voltage " + MMC_DBG_SW_VOL_FUNC(host->mmc, "%s: Switching to 1.8V signalling voltage " " failed\n", mmc_hostname(host->mmc)); return -EIO; } + dw_mci_do_grf_io_domain_switch(host, IO_DOMAIN_18); } /* * Enable 1.8V Signal Enable in the Host Control2 * register */ - mci_writel(host,UHS_REG , uhs_reg | SDMMC_UHS_VOLT_REG_18); + mci_writel(host,UHS_REG , uhs_reg | SDMMC_UHS_VOLT_REG_18); /* Wait for 5ms */ usleep_range(5000, 5500); - MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s: .[%s]\n",__LINE__, __FUNCTION__,mmc_hostname(host->mmc)); + MMC_DBG_SW_VOL_FUNC(host->mmc,"%d..%s: .[%s]\n",__LINE__, + __FUNCTION__,mmc_hostname(host->mmc)); /* 1.8V regulator output should be stable within 5 ms */ uhs_reg = mci_readl(host, UHS_REG); - if( uhs_reg & SDMMC_UHS_VOLT_REG_18){ - - return 0; - } - - pr_warning("%s: 1.8V regulator output did not became stable\n", + if(uhs_reg & SDMMC_UHS_VOLT_REG_18) + return 0; + + MMC_DBG_SW_VOL_FUNC(host->mmc, "%s: 1.8V regulator output did not became stable\n", mmc_hostname(host->mmc)); return -EAGAIN; @@ -1433,7 +1831,7 @@ static int dw_mci_do_start_signal_voltage_switch(struct dw_mci *host, if (host->vmmc) { ret = io_domain_regulator_set_voltage(host->vmmc, 1200000, 1200000); if (ret) { - pr_warning("%s: Switching to 1.2V signalling voltage " + MMC_DBG_SW_VOL_FUNC(host->mmc, "%s: Switching to 1.2V signalling voltage " " failed\n", mmc_hostname(host->mmc)); return -EIO; } @@ -1455,12 +1853,13 @@ static int dw_mci_start_signal_voltage_switch(struct mmc_host *mmc, if (host->verid < DW_MMC_240A) return 0; - //sdhci_runtime_pm_get(host); + err = dw_mci_do_start_signal_voltage_switch(host, ios); - //sdhci_runtime_pm_put(host); return err; } +#endif + static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct dw_mci_slot *slot = mmc_priv(mmc); @@ -1469,6 +1868,10 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) struct dw_mci_tuning_data tuning_data; int err = -ENOSYS; + /* Fixme: 3036/3126 doesn't support 1.8 io domain, no sense exe tuning */ + if(cpu_is_rk3036() || cpu_is_rk312x()) + return err; + if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { tuning_data.blk_pattern = tuning_blk_pattern_8bit; @@ -1488,19 +1891,26 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) return -EINVAL; } - ///////////////////////////////////////////////// - //temporary settings,!!!!!!!!!!!!!!! + + /* Recommend sample phase and delayline + Fixme: Mix-use these three controllers will cause + con_id mismatch. + */ if (mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC) tuning_data.con_id = 3; - else if (mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) + else if(mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) tuning_data.con_id = 1; else - tuning_data.con_id = 0; - tuning_data.tuning_type = 1; //0--drv, 1--sample - ///////////////////////////////////////////////// + tuning_data.con_id = 0; + /* 0: driver, from host->devices + 1: sample, from devices->host + */ + tuning_data.tuning_type = 1; + if (drv_data && drv_data->execute_tuning) err = drv_data->execute_tuning(slot, opcode, &tuning_data); + return err; } @@ -1515,10 +1925,13 @@ static const struct mmc_host_ops dw_mci_ops = { .hw_reset = dw_mci_hw_reset, .enable_sdio_irq = dw_mci_enable_sdio_irq, .execute_tuning = dw_mci_execute_tuning, - //.start_signal_voltage_switch = dw_mci_start_signal_voltage_switch, - .card_busy = dw_mci_wait_unbusy, + #ifdef CONFIG_MMC_DW_ROCKCHIP_SWITCH_VOLTAGE + .start_signal_voltage_switch = dw_mci_start_signal_voltage_switch, + .card_busy = dw_mci_card_busy, + #endif }; +#if 0 static void dw_mci_enable_irq(struct dw_mci *host, bool irqflag) { unsigned long flags; @@ -1541,12 +1954,13 @@ static void dw_mci_enable_irq(struct dw_mci *host, bool irqflag) } local_irq_restore(flags); } +#endif static void dw_mci_deal_data_end(struct dw_mci *host, struct mmc_request *mrq) __releases(&host->lock) __acquires(&host->lock) { - if(DW_MCI_SEND_STATUS == host->dir_status){ + if(DW_MCI_SEND_STATUS == host->dir_status){ #if 0 if( MMC_BUS_TEST_W != host->cmd->opcode){ if(host->data_status & SDMMC_INT_DCRC) @@ -1554,13 +1968,12 @@ static void dw_mci_deal_data_end(struct dw_mci *host, struct mmc_request *mrq) else if(host->data_status & SDMMC_INT_EBE) host->data->error = -ETIMEDOUT; } else { - dw_mci_wait_unbusy(host->mmc); + dw_mci_wait_unbusy(host); } #else - dw_mci_wait_unbusy(host->mmc); + dw_mci_wait_unbusy(host); #endif - - } + } } static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -1571,15 +1984,16 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) struct mmc_host *prev_mmc = host->cur_slot->mmc; WARN_ON(host->cmd || host->data); - - dw_mci_deal_data_end(host, mrq); - if(mrq->cmd) - MMC_DBG_CMD_FUNC(host->mmc, " reqeust end--reqeuest done, cmd=%d, cmderr=%d, host->state=%d [%s]",\ - mrq->cmd->opcode,mrq->cmd->error, host->state,mmc_hostname(host->mmc)); - if(mrq->data) - MMC_DBG_CMD_FUNC(host->mmc, " reqeust end--reqeuest done, cmd=%d, dataerr=%d, host->state=%d [%s]",\ - mrq->cmd->opcode,mrq->data->error, host->state, mmc_hostname(host->mmc)); + del_timer_sync(&host->dto_timer); + dw_mci_deal_data_end(host, mrq); + + if(mrq->cmd) + MMC_DBG_CMD_FUNC(host->mmc, " reqeust end--reqeuest done, cmd=%d, cmderr=%d, host->state=%d [%s]",\ + mrq->cmd->opcode,mrq->cmd->error, host->state,mmc_hostname(host->mmc)); + if(mrq->data) + MMC_DBG_CMD_FUNC(host->mmc, " reqeust end--reqeuest done, cmd=%d, dataerr=%d, host->state=%d [%s]",\ + mrq->cmd->opcode,mrq->data->error, host->state, mmc_hostname(host->mmc)); host->cur_slot->mrq = NULL; host->mrq = NULL; @@ -1597,7 +2011,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) host->state = STATE_IDLE; } - spin_unlock(&host->lock); + spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); } @@ -1632,24 +2046,29 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd { if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) host->cmd_rto += 1; - - cmd->error = -ETIMEDOUT; - } - else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)) + + cmd->error = -ETIMEDOUT; + del_timer_sync(&host->dto_timer); + }else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)){ + del_timer_sync(&host->dto_timer); cmd->error = -EILSEQ; - else if (status & SDMMC_INT_RESP_ERR) + }else if (status & SDMMC_INT_RESP_ERR){ + del_timer_sync(&host->dto_timer); cmd->error = -EIO; - else + }else{ cmd->error = 0; - MMC_DBG_CMD_FUNC(host->mmc, " command complete, cmd=%d,cmdError=%d [%s]",cmd->opcode, cmd->error,mmc_hostname(host->mmc)); + } + MMC_DBG_CMD_FUNC(host->mmc, " command complete, cmd=%d,cmdError=%d [%s]", + cmd->opcode, cmd->error,mmc_hostname(host->mmc)); if (cmd->error) { + del_timer_sync(&host->dto_timer); if(MMC_SEND_STATUS != cmd->opcode) if(host->cmd_rto >= SDMMC_CMD_RTO_MAX_HOLD){ - MMC_DBG_ERR_FUNC(host->mmc, " command complete, cmd=%d,cmdError=%d [%s]",\ - cmd->opcode, cmd->error,mmc_hostname(host->mmc)); - host->cmd_rto = 0; - } + MMC_DBG_ERR_FUNC(host->mmc, " command complete, cmd=%d,cmdError=%d [%s]",\ + cmd->opcode, cmd->error,mmc_hostname(host->mmc)); + host->cmd_rto = 0; + } /* newer ip versions need a delay between retries */ if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) @@ -1661,12 +2080,14 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; - struct dw_mci_slot *slot = mmc_priv(host->mmc); + struct dw_mci_slot *slot = mmc_priv(host->mmc); struct mmc_data *data; struct mmc_command *cmd; enum dw_mci_state state; enum dw_mci_state prev_state; - u32 status, ctrl; + u32 status, cmd_flags; + unsigned long timeout = 0; + bool ret = true; spin_lock(&host->lock); @@ -1696,24 +2117,25 @@ static void dw_mci_tasklet_func(unsigned long priv) goto unlock; } - if (cmd->data && cmd->error) { + if (cmd->data && cmd->error) { + del_timer_sync(&host->dto_timer); /* delete the timer for INT_DTO */ dw_mci_stop_dma(host); #if 1 - if (data->stop) { - send_stop_cmd(host, data); - state = STATE_SENDING_STOP; - break; - } else { - host->data = NULL; - } + if (data->stop) { + send_stop_cmd(host, data); + state = STATE_SENDING_STOP; + break; + }else{ + host->data = NULL; + } #else send_stop_abort(host, data); state = STATE_SENDING_STOP; break; #endif + set_bit(EVENT_DATA_COMPLETE, &host->completed_events); } - if (!host->mrq->data || cmd->error) { dw_mci_request_end(host, host->mrq); goto unlock; @@ -1723,26 +2145,55 @@ static void dw_mci_tasklet_func(unsigned long priv) /* fall through */ case STATE_SENDING_DATA: - if (test_and_clear_bit(EVENT_DATA_ERROR, - &host->pending_events)) { + if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); #if 1 - if (data->stop) + if (data->stop){ send_stop_cmd(host, data); + }else{ + /*single block read/write, send stop cmd manually to prevent host controller halt*/ + MMC_DBG_INFO_FUNC(host->mmc, "%s status 1 0x%08x [%s]\n", + __func__, mci_readl(host, STATUS), mmc_hostname(host->mmc)); + + mci_writel(host, CMDARG, 0); + wmb(); + cmd_flags = SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC + | SDMMC_CMD_RESP_EXP | MMC_STOP_TRANSMISSION; + + if(host->mmc->hold_reg_flag) + cmd_flags |= SDMMC_CMD_USE_HOLD_REG; + + mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); + wmb(); + timeout = jiffies + msecs_to_jiffies(500); + + while(ret){ + ret = time_before(jiffies, timeout); + if(!(mci_readl(host, CMD) & SDMMC_CMD_START)) + break; + } + if(false == ret) + MMC_DBG_ERR_FUNC(host->mmc, + "%s EVENT_DATA_ERROR recovery failed!!! [%s]\n", + __func__, mmc_hostname(host->mmc)); + } #else send_stop_abort(host, data); #endif state = STATE_DATA_ERROR; break; } - MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: STATE_SENDING_DATA, wait for EVENT_XFER_COMPLETE.[%s]",\ + + MMC_DBG_CMD_FUNC(host->mmc, + "Pre-state[%d]-->NowState[%d]: STATE_SENDING_DATA, wait for EVENT_XFER_COMPLETE.[%s]",\ prev_state,state, mmc_hostname(host->mmc)); if (!test_and_clear_bit(EVENT_XFER_COMPLETE, &host->pending_events)) break; - MMC_DBG_INFO_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: STATE_SENDING_DATA, wait for EVENT_DATA_COMPLETE. [%s]",\ - prev_state,state,mmc_hostname(host->mmc)); + MMC_DBG_INFO_FUNC(host->mmc, + "Pre-state[%d]-->NowState[%d]: STATE_SENDING_DATA, wait for EVENT_DATA_COMPLETE. [%s]",\ + prev_state,state,mmc_hostname(host->mmc)); set_bit(EVENT_XFER_COMPLETE, &host->completed_events); prev_state = state = STATE_DATA_BUSY; @@ -1754,8 +2205,10 @@ static void dw_mci_tasklet_func(unsigned long priv) break; dw_mci_deal_data_end(host, host->mrq); - MMC_DBG_INFO_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: STATE_DATA_BUSY, after EVENT_DATA_COMPLETE. [%s]", \ - prev_state,state,mmc_hostname(host->mmc)); + del_timer_sync(&host->dto_timer); //delete the timer for INT_DTO + MMC_DBG_INFO_FUNC(host->mmc, + "Pre-state[%d]-->NowState[%d]: STATE_DATA_BUSY, after EVENT_DATA_COMPLETE. [%s]", \ + prev_state,state,mmc_hostname(host->mmc)); host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); @@ -1763,7 +2216,8 @@ static void dw_mci_tasklet_func(unsigned long priv) if (status & DW_MCI_DATA_ERROR_FLAGS) { if((SDMMC_CTYPE_1BIT != slot->ctype)&&(MMC_SEND_EXT_CSD == host->mrq->cmd->opcode)) - MMC_DBG_ERR_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: DW_MCI_DATA_ERROR_FLAGS,datastatus=0x%x [%s]",\ + MMC_DBG_ERR_FUNC(host->mmc, + "Pre-state[%d]-->NowState[%d]: DW_MCI_DATA_ERROR_FLAGS,datastatus=0x%x [%s]",\ prev_state,state, status, mmc_hostname(host->mmc)); if (status & SDMMC_INT_DRTO) { @@ -1771,8 +2225,7 @@ static void dw_mci_tasklet_func(unsigned long priv) } else if (status & SDMMC_INT_DCRC) { data->error = -EILSEQ; } else if (status & SDMMC_INT_EBE && - host->dir_status == - DW_MCI_SEND_STATUS) { + host->dir_status == DW_MCI_SEND_STATUS){ /* * No data CRC status was returned. * The number of bytes transferred will @@ -1800,19 +2253,19 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!data->stop) { - MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: no stop and no dataerr, exit. [%s]", \ - prev_state,state,mmc_hostname(host->mmc)); + MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: no stop and no dataerr, exit. [%s]", \ + prev_state,state,mmc_hostname(host->mmc)); dw_mci_request_end(host, host->mrq); goto unlock; } - MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: begin to stop . [%s]", \ - prev_state,state,mmc_hostname(host->mmc)); + MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: begin to stop . [%s]", \ + prev_state,state,mmc_hostname(host->mmc)); if (host->mrq->sbc && !data->error) { data->stop->error = 0; - MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: have stop and sbc, exit. [%s]", \ - prev_state,state,mmc_hostname(host->mmc)); + MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: have stop and sbc, exit. [%s]", \ + prev_state,state,mmc_hostname(host->mmc)); dw_mci_request_end(host, host->mrq); goto unlock; @@ -1829,31 +2282,31 @@ static void dw_mci_tasklet_func(unsigned long priv) } #endif /* fall through */ - MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: begin to STATE_SENDING_STOP . [%s]", \ - prev_state,state,mmc_hostname(host->mmc)); + MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: begin to STATE_SENDING_STOP . [%s]", \ + prev_state,state,mmc_hostname(host->mmc)); case STATE_SENDING_STOP: - if (!test_and_clear_bit(EVENT_CMD_COMPLETE, - &host->pending_events)) + if (!test_and_clear_bit(EVENT_CMD_COMPLETE, &host->pending_events)) break; - MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: begin to send cmd12 . [%s]", \ - prev_state,state,mmc_hostname(host->mmc)); + + MMC_DBG_CMD_FUNC(host->mmc, "Pre-state[%d]-->NowState[%d]: begin to send cmd12 . [%s]", \ + prev_state, state, mmc_hostname(host->mmc)); - /* CMD error in data command */ + /* CMD error in data command */ if (host->mrq->cmd->error && host->mrq->data) { dw_mci_fifo_reset(host); } host->cmd = NULL; host->data = NULL; - #if 1 - dw_mci_command_complete(host, host->mrq->stop); - #else + #if 1 + dw_mci_command_complete(host, host->mrq->stop); + #else if (host->mrq->stop) dw_mci_command_complete(host, host->mrq->stop); else host->cmd_status = 0; - #endif + #endif dw_mci_request_end(host, host->mrq); goto unlock; @@ -2187,6 +2640,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host, bool dto) unsigned int len; unsigned int remain, fcnt; + if(!host->mmc->bus_refs){ + printk("Note: %s host->mmc->bus_refs is 0!!!\n", __func__); + goto host_put; + } do { if (!sg_miter_next(sg_miter)) goto done; @@ -2225,6 +2682,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host, bool dto) done: sg_miter_stop(sg_miter); +host_put: host->sg = NULL; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); @@ -2241,6 +2699,11 @@ static void dw_mci_write_data_pio(struct dw_mci *host) unsigned int len; unsigned int fifo_depth = host->fifo_depth; unsigned int remain, fcnt; + + if(!host->mmc->bus_refs){ + printk("Note: %s host->mmc->bus_refs is 0!!!\n", __func__); + goto host_put; + } do { if (!sg_miter_next(sg_miter)) @@ -2279,6 +2742,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host) done: sg_miter_stop(sg_miter); +host_put: host->sg = NULL; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); @@ -2286,12 +2750,27 @@ done: static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) { - if (!host->cmd_status) + u32 multi, unit; + + if (!host->cmd_status) host->cmd_status = status; - smp_wmb(); + if(!host->cmd) + goto cmd_exit; + + if((MMC_STOP_TRANSMISSION != host->cmd->opcode)) + { + unit = 2*1024*1024; + multi = mci_readl(host, BYTCNT)/unit; + multi += ((mci_readl(host, 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(4500 * multi));//max wait 8s larger + } - set_bit(EVENT_CMD_COMPLETE, &host->pending_events); +cmd_exit: + smp_wmb(); + set_bit(EVENT_CMD_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); } @@ -2304,10 +2783,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) pending = mci_readl(host, MINTSTS); /* read-only mask reg */ /* - * DTO fix - version 2.10a and below, and only if internal DMA - * is configured. - */ - if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { + * DTO fix - version 2.10a and below, and only if internal DMA + * is configured. + */ + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { if (!pending && ((mci_readl(host, STATUS) >> 17) & 0x1fff)) pending |= SDMMC_INT_DATA_OVER; @@ -2319,7 +2798,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) host->cmd_status = pending; smp_wmb(); MMC_DBG_INFO_FUNC(host->mmc,"Line%d..%s cmd_status INT=0x%x,[%s]", - __LINE__, __FUNCTION__,host->cmd_status,mmc_hostname(host->mmc)); + __LINE__, __FUNCTION__,host->cmd_status,mmc_hostname(host->mmc)); set_bit(EVENT_CMD_COMPLETE, &host->pending_events); } @@ -2332,12 +2811,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) set_bit(EVENT_DATA_ERROR, &host->pending_events); MMC_DBG_INFO_FUNC(host->mmc,"Line%d..%s data_status INT=0x%x,[%s]", - __LINE__, __FUNCTION__,host->data_status,mmc_hostname(host->mmc)); + __LINE__, __FUNCTION__,host->data_status,mmc_hostname(host->mmc)); tasklet_schedule(&host->tasklet); } if (pending & SDMMC_INT_DATA_OVER) { mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); + del_timer(&host->dto_timer); /* delete the timer for INT_DTO */ MMC_DBG_CMD_FUNC(host->mmc, "SDMMC_INT_DATA_OVER, INT-pending=0x%x. [%s]",pending,mmc_hostname(host->mmc)); if (!host->data_status) host->data_status = pending; @@ -2363,30 +2843,38 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending & SDMMC_INT_VSI) { - MMC_DBG_SW_VOL_FUNC(host->mmc, "SDMMC_INT_VSI, INT-pending=0x%x. [%s]",pending,mmc_hostname(host->mmc)); + MMC_DBG_SW_VOL_FUNC(host->mmc, "SDMMC_INT_VSI, INT-pending=0x%x. [%s]",pending,mmc_hostname(host->mmc)); mci_writel(host, RINTSTS, SDMMC_INT_VSI); dw_mci_cmd_interrupt(host, pending); } if (pending & SDMMC_INT_CMD_DONE) { - MMC_DBG_CMD_FUNC(host->mmc, "SDMMC_INT_CMD_DONE, INT-pending=0x%x. [%s]",pending,mmc_hostname(host->mmc)); + MMC_DBG_CMD_FUNC(host->mmc, "SDMMC_INT_CMD_DONE, CMD = 0x%x, INT-pending=0x%x. [%s]",mci_readl(host, CMD),pending,mmc_hostname(host->mmc)); mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); dw_mci_cmd_interrupt(host, pending); } if (pending & SDMMC_INT_CD) { mci_writel(host, RINTSTS, SDMMC_INT_CD); + MMC_DBG_INFO_FUNC(host->mmc, "SDMMC_INT_CD, INT-pending=0x%x. [%s]", pending, mmc_hostname(host->mmc)); + wake_lock_timeout(&host->mmc->detect_wake_lock, 5 * HZ); queue_work(host->card_workqueue, &host->card_work); } + + if (pending & SDMMC_INT_HLE) { + mci_writel(host, RINTSTS, SDMMC_INT_HLE); + MMC_DBG_CMD_FUNC(host->mmc, "SDMMC_INT_HLE INT-pending=0x%x. [%s]\n",pending,mmc_hostname(host->mmc)); + + } /* Handle SDIO Interrupts */ for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; - if (host->verid < DW_MMC_240A) - sdio_int = SDMMC_INT_SDIO(i); - else - sdio_int = SDMMC_INT_SDIO(i + 8); + if (host->verid < DW_MMC_240A) + sdio_int = SDMMC_INT_SDIO(i); + else + sdio_int = SDMMC_INT_SDIO(i + 8); if (pending & sdio_int) { mci_writel(host, RINTSTS, sdio_int); @@ -2397,13 +2885,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } #ifdef CONFIG_MMC_DW_IDMAC - /* Handle DMA interrupts */ - pending = mci_readl(host, IDSTS); - if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { - mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); - mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete(host); - } + /* External DMA Soc platform NOT need to ack interrupt IDSTS */ + if(!(cpu_is_rk3036() || cpu_is_rk312x())){ + /* Handle DMA interrupts */ + pending = mci_readl(host, IDSTS); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete((void *)host); + } + } #endif return IRQ_HANDLED; @@ -2419,17 +2910,18 @@ static void dw_mci_work_routine_card(struct work_struct *work) struct mmc_host *mmc = slot->mmc; struct mmc_request *mrq; int present; - u32 ctrl; present = dw_mci_get_cd(mmc); while (present != slot->last_detect_state) { dev_dbg(&slot->mmc->class_dev, "card %s\n", present ? "inserted" : "removed"); - MMC_DBG_BOOT_FUNC(mmc, " The card is %s. ===!!!!!!==[%s]\n", + MMC_DBG_BOOT_FUNC(mmc, " The card is %s. ===!!!!!!==[%s]\n", present ? "inserted" : "removed.", mmc_hostname(mmc)); - + + rk_send_wakeup_key();//wake up system spin_lock_bh(&host->lock); + del_timer(&host->dto_timer); /* delete the timer for INT_DTO */ /* Card change detected */ slot->last_detect_state = present; @@ -2473,7 +2965,8 @@ static void dw_mci_work_routine_card(struct work_struct *work) if (mrq->stop) mrq->stop->error = -ENOMEDIUM; - MMC_DBG_CMD_FUNC(host->mmc, "dw_mci_work--reqeuest done, cmd=%d [%s]",mrq->cmd->opcode, mmc_hostname(mmc)); + MMC_DBG_CMD_FUNC(host->mmc, "dw_mci_work--reqeuest done, cmd=%d [%s]", + mrq->cmd->opcode, mmc_hostname(mmc)); spin_unlock(&host->lock); mmc_request_done(slot->mmc, mrq); @@ -2486,7 +2979,8 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Clear down the FIFO */ dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC - dw_mci_idmac_reset(host); + if(!(cpu_is_rk3036() || cpu_is_rk312x())) + dw_mci_idmac_reset(host); #endif } @@ -2497,7 +2991,7 @@ static void dw_mci_work_routine_card(struct work_struct *work) } mmc_detect_change(slot->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); + msecs_to_jiffies(host->pdata->detect_delay_ms)); } } @@ -2615,7 +3109,7 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, struct mmc_host *mmc) { - struct device_node *np = dw_mci_of_find_slot_node(dev, slot); + struct device_node *np = dev->of_node;//dw_mci_of_find_slot_node(dev, slot); int gpio; if (!np) @@ -2630,6 +3124,42 @@ static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, if (mmc_gpio_request_cd(mmc, gpio, 0)) dev_warn(dev, "gpio [%d] request failed\n", gpio); } + +static irqreturn_t dw_mci_gpio_cd_irqt(int irq, void *dev_id) +{ + struct mmc_host *mmc = dev_id; + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + + queue_work(host->card_workqueue, &host->card_work); + + return IRQ_HANDLED; +} + +static void dw_mci_of_set_cd_gpio_irq(struct device *dev, u32 gpio, + struct mmc_host *mmc) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + int irq; + int ret; + + /* Having a missing entry is valid; return silently */ + if (!gpio_is_valid(gpio)) + return; + + irq = gpio_to_irq(gpio); + if (irq >= 0) { + ret = devm_request_threaded_irq(&mmc->class_dev, irq, + NULL, dw_mci_gpio_cd_irqt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "dw_mci_cd", mmc); + if (ret < 0) + irq = ret; + } else { + dev_err(host->dev, "Request cd-gpio interrupt error!\n"); + } +} #else /* CONFIG_OF */ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) { @@ -2703,6 +3233,20 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (of_find_property(host->dev->of_node, "supports-emmc", NULL)) mmc->restrict_caps |= RESTRICT_CARD_TYPE_EMMC; + /* We assume only low-level chip use gpio_cd */ + if (cpu_is_rk312x() && + soc_is_rk3126() && + (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)) { + slot->cd_gpio = of_get_named_gpio(host->dev->of_node, "cd-gpios", 0); + if (gpio_is_valid(slot->cd_gpio)) { + /* Request gpio int for card detection */ + dw_mci_of_set_cd_gpio_irq(host->dev, slot->cd_gpio,host->mmc); + } else { + slot->cd_gpio = -ENODEV; + dev_err(host->dev, "failed to get your cd-gpios!\n"); + } + } + if (host->pdata->get_ocr) mmc->ocr_avail = host->pdata->get_ocr(id); else @@ -2738,8 +3282,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (drv_data && drv_data->hold_reg_flag) mmc->hold_reg_flag |= drv_data->hold_reg_flag[ctrl_id]; - //set the compatibility of driver. - mmc->caps |= MMC_CAP_UHS_SDR12|MMC_CAP_UHS_SDR25|MMC_CAP_UHS_SDR50|MMC_CAP_UHS_SDR104|MMC_CAP_ERASE; + /* set the compatibility of driver. */ + mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 + | MMC_CAP_UHS_SDR104 | MMC_CAP_ERASE ; if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; @@ -2752,13 +3297,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) bus_width = 1; switch (bus_width) { - case 8: - mmc->caps |= MMC_CAP_8_BIT_DATA; - case 4: - mmc->caps |= MMC_CAP_4_BIT_DATA; + case 8: + mmc->caps |= MMC_CAP_8_BIT_DATA; + case 4: + mmc->caps |= MMC_CAP_4_BIT_DATA; } + if (of_find_property(host->dev->of_node, "cap-power-off-card", NULL)) - mmc->caps |= MMC_CAP_POWER_OFF_CARD; + mmc->caps |= MMC_CAP_POWER_OFF_CARD; if (of_find_property(host->dev->of_node, "cap-sdio-irq", NULL)) mmc->caps |= MMC_CAP_SDIO_IRQ; if (of_find_property(host->dev->of_node, "poll-hw-reset", NULL)) @@ -2767,12 +3313,12 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->caps2 |= MMC_CAP2_FULL_PWR_CYCLE; if (of_find_property(host->dev->of_node, "keep-power-in-suspend", NULL)) mmc->pm_caps |= MMC_PM_KEEP_POWER; - if (of_find_property(host->dev->of_node, "ignore-pm-notify", NULL)) - mmc->pm_caps |= MMC_PM_IGNORE_PM_NOTIFY; + if (of_find_property(host->dev->of_node, "ignore-pm-notify", NULL)) + mmc->pm_caps |= MMC_PM_IGNORE_PM_NOTIFY; if (of_find_property(host->dev->of_node, "enable-sdio-wakeup", NULL)) mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - /*Assign pm_caps pass to pm_flags*/ + /*Assign pm_caps pass to pm_flags*/ mmc->pm_flags = mmc->pm_caps; if (host->pdata->blk_settings) { @@ -2789,6 +3335,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->max_blk_count = host->ring_size; mmc->max_seg_size = 0x1000; mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; + if(cpu_is_rk3036() || cpu_is_rk312x()){ + /* fixup for external dmac setting */ + mmc->max_segs = 64; + mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ + mmc->max_blk_count = 65535; + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + } #else mmc->max_segs = 64; mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ @@ -2796,67 +3350,74 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; #endif /* CONFIG_MMC_DW_IDMAC */ + } - //pwr_en - slot->pwr_en_gpio = dw_mci_of_get_pwr_en_gpio(host->dev, slot->id); + /* pwr_en */ + slot->pwr_en_gpio = dw_mci_of_get_pwr_en_gpio(host->dev, slot->id); - if (!(mmc->restrict_caps & RESTRICT_CARD_TYPE_SD))//(gpio_is_valid(slot->pwr_en_gpio)) - { - host->vmmc = NULL; - }else{ - - if(mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) + if (!(mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)) { - host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc"); - } - else - { - host->vmmc = NULL; - } + host->vmmc = NULL; + }else{ + + if(mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) + host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc"); + else + host->vmmc = NULL; - if (IS_ERR(host->vmmc)) { - pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); - host->vmmc = NULL; - } else { - ret = regulator_enable(host->vmmc); - if (ret) { - dev_err(host->dev, - "failed to enable regulator: %d\n", ret); - goto err_setup_bus; - } - } - } + if (IS_ERR(host->vmmc)) { + pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); + host->vmmc = NULL; + }else{ + ret = regulator_enable(host->vmmc); + if (ret) { + dev_err(host->dev, + "failed to enable regulator: %d\n", ret); + host->vmmc = NULL; + goto err_setup_bus; + } + } + } - slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); - dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc); + slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); - if (mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); + if (mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - ret = mmc_add_host(mmc); - if (ret) - goto err_setup_bus; - - /* Pinctrl set default iomux state to fucntion port. - * Fixme: DON'T TOUCH EMMC SETTING! - */ - if(!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)) - { - host->pinctrl = devm_pinctrl_get(host->dev); - if(IS_ERR(host->pinctrl)) - printk("%s: Warning : No pinctrl used!\n",mmc_hostname(host->mmc)); - else + ret = mmc_add_host(mmc); + if (ret) + goto err_setup_bus; + + /* Pinctrl set default iomux state to fucntion port. + * Fixme: DON'T TOUCH EMMC SETTING! + */ + if(!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)) { - host->pins_default = pinctrl_lookup_state(host->pinctrl,PINCTRL_STATE_DEFAULT); - if(IS_ERR(host->pins_default)) - printk("%s: Warning : No default pinctrl matched!\n", mmc_hostname(host->mmc)); - else - { - if(pinctrl_select_state(host->pinctrl, host->pins_default) < 0) - printk("%s: Warning : Default pinctrl setting failed!\n", mmc_hostname(host->mmc)); - } - } - } + host->pinctrl = devm_pinctrl_get(host->dev); + if(IS_ERR(host->pinctrl)){ + printk("%s: Warning : No pinctrl used!\n",mmc_hostname(host->mmc)); + }else{ + host->pins_idle= pinctrl_lookup_state(host->pinctrl,PINCTRL_STATE_IDLE); + if(IS_ERR(host->pins_default)){ + printk("%s: Warning : No IDLE pinctrl matched!\n", mmc_hostname(host->mmc)); + } + else + { + if(pinctrl_select_state(host->pinctrl, host->pins_idle) < 0) + printk("%s: Warning : Idle pinctrl setting failed!\n", mmc_hostname(host->mmc)); + } + + host->pins_default = pinctrl_lookup_state(host->pinctrl,PINCTRL_STATE_DEFAULT); + if(IS_ERR(host->pins_default)){ + printk("%s: Warning : No default pinctrl matched!\n", mmc_hostname(host->mmc)); + } + else + { + if(pinctrl_select_state(host->pinctrl, host->pins_default) < 0) + printk("%s: Warning : Default pinctrl setting failed!\n", mmc_hostname(host->mmc)); + } + } + } #if defined(CONFIG_DEBUG_FS) @@ -2897,9 +3458,14 @@ static void dw_mci_init_dma(struct dw_mci *host) } /* Determine which DMA interface to use */ -#ifdef CONFIG_MMC_DW_IDMAC - host->dma_ops = &dw_mci_idmac_ops; - dev_info(host->dev, "Using internal DMA controller.\n"); +#if defined(CONFIG_MMC_DW_IDMAC) + if(cpu_is_rk3036() || cpu_is_rk312x()){ + host->dma_ops = &dw_mci_edmac_ops; + dev_info(host->dev, "Using external DMA controller.\n"); + }else{ + host->dma_ops = &dw_mci_idmac_ops; + dev_info(host->dev, "Using internal DMA controller.\n"); + } #endif if (!host->dma_ops) @@ -2939,13 +3505,13 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) do { ctrl = mci_readl(host, CTRL); if (!(ctrl & reset)) - return true; + return true; } while (time_before(jiffies, timeout)); dev_err(host->dev, "Timeout resetting block (ctrl reset %#x)\n", ctrl & reset); - + return false; } @@ -2972,6 +3538,7 @@ static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host) } #ifdef CONFIG_OF +/* static struct dw_mci_of_quirks { char *quirk; int id; @@ -2981,14 +3548,14 @@ static struct dw_mci_of_quirks { .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, }, }; - +*/ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) { struct dw_mci_board *pdata; struct device *dev = host->dev; struct device_node *np = dev->of_node; const struct dw_mci_drv_data *drv_data = host->drv_data; - int idx, ret; + int ret; u32 clock_frequency; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -3065,6 +3632,73 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) } #endif /* CONFIG_OF */ +static void dw_mci_dealwith_timeout(struct dw_mci *host) +{ + u32 ret, i, regs; + + switch(host->state){ + case STATE_IDLE: + break; + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + host->data_status |= (SDMMC_INT_DCRC|SDMMC_INT_EBE); + mci_writel(host, RINTSTS, SDMMC_INT_DRTO); // clear interrupt + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + host->state = STATE_DATA_BUSY; + if (!dw_mci_ctrl_all_reset(host)) { + ret = -ENODEV; + return ; + } + + /* NO requirement to reclaim slave chn using external dmac */ + #ifdef CONFIG_MMC_DW_IDMAC + if(!(cpu_is_rk3036() || cpu_is_rk312x())) + if (host->use_dma && host->dma_ops->init) + host->dma_ops->init(host); + #endif + + /* + * Restore the initial value at FIFOTH register + * And Invalidate the prev_blksz with zero + */ + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + mci_writel(host, TMOUT, 0xFFFFFFFF); + mci_writel(host, RINTSTS, 0xFFFFFFFF); + regs = SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR + | SDMMC_INT_RXDR | SDMMC_INT_VSI | DW_MCI_ERROR_FLAGS; + if(!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO)) + regs |= SDMMC_INT_CD; + mci_writel(host, INTMASK, regs); + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot[i]; + if (!slot) + continue; + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + dw_mci_set_ios(slot->mmc, &slot->mmc->ios); + dw_mci_setup_bus(slot, true); + } + } + mci_writel(host, RINTSTS, 0xFFFFFFFF); + tasklet_schedule(&host->tasklet); + break; + default: + break; + } +} +static void dw_mci_dto_timeout(unsigned long host_data) +{ + struct dw_mci *host = (struct dw_mci *) host_data; + + disable_irq(host->irq); + + host->data_status = SDMMC_INT_EBE; + mci_writel(host, RINTSTS, 0xFFFFFFFF); + dw_mci_dealwith_timeout(host); + + enable_irq(host->irq); +} int dw_mci_probe(struct dw_mci *host) { const struct dw_mci_drv_data *drv_data = host->drv_data; @@ -3099,39 +3733,42 @@ int dw_mci_probe(struct dw_mci *host) else host->data_offset = DATA_240A_OFFSET; - //hclk enable - host->hclk_mmc= devm_clk_get(host->dev, "hclk_mmc"); - if (IS_ERR(host->hclk_mmc)) { - dev_err(host->dev, "failed to get hclk_mmc\n"); - ret = PTR_ERR(host->hclk_mmc); - goto err_hclk_mmc; - } - clk_prepare_enable(host->hclk_mmc); - - //mmc clk enable - host->clk_mmc = devm_clk_get(host->dev, "clk_mmc"); - if (IS_ERR(host->clk_mmc)) { - dev_err(host->dev, "failed to get clk mmc_per\n"); - ret = PTR_ERR(host->clk_mmc); - goto err_clk_mmc; - } + //hclk enable + host->hclk_mmc= devm_clk_get(host->dev, "hclk_mmc"); + if (IS_ERR(host->hclk_mmc)) { + dev_err(host->dev, "failed to get hclk_mmc\n"); + ret = PTR_ERR(host->hclk_mmc); + goto err_hclk_mmc; + } + + clk_prepare_enable(host->hclk_mmc); + + //mmc clk enable + host->clk_mmc = devm_clk_get(host->dev, "clk_mmc"); + if (IS_ERR(host->clk_mmc)) { + dev_err(host->dev, "failed to get clk mmc_per\n"); + ret = PTR_ERR(host->clk_mmc); + goto err_clk_mmc; + } - if (host->verid < DW_MMC_240A) - host->bus_hz = host->pdata->bus_hz; - else - host->bus_hz = host->pdata->bus_hz*2;// *2 due to fix divider 2 in controller. - if (!host->bus_hz) { - dev_err(host->dev,"Platform data must supply bus speed\n"); - ret = -ENODEV; - goto err_clk_mmc; - } + host->bus_hz = host->pdata->bus_hz; + if (!host->bus_hz) { + dev_err(host->dev,"Platform data must supply bus speed\n"); + ret = -ENODEV; + goto err_clk_mmc; + } - ret = clk_set_rate(host->clk_mmc, host->bus_hz); + if (host->verid < DW_MMC_240A) + ret = clk_set_rate(host->clk_mmc, host->bus_hz); + else + //rockchip: fix divider 2 in clksum before controlller + ret = clk_set_rate(host->clk_mmc, host->bus_hz * 2); + if(ret < 0) { dev_err(host->dev, "failed to set clk mmc\n"); goto err_clk_mmc; } - clk_prepare_enable(host->clk_mmc); + clk_prepare_enable(host->clk_mmc); if (drv_data && drv_data->setup_clock) { ret = drv_data->setup_clock(host); @@ -3142,10 +3779,11 @@ int dw_mci_probe(struct dw_mci *host) } } - host->quirks = host->pdata->quirks; - host->irq_state = true; - host->set_speed = 0; - host->set_div = 0; + host->quirks = host->pdata->quirks; + host->irq_state = true; + host->set_speed = 0; + host->set_div = 0; + host->svi_flags = 0; spin_lock_init(&host->lock); INIT_LIST_HEAD(&host->queue); @@ -3233,6 +3871,7 @@ int dw_mci_probe(struct dw_mci *host) else host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; + setup_timer(&host->dto_timer, dw_mci_dto_timeout, (unsigned long)host); /* We need at least one slot to succeed */ for (i = 0; i < host->num_slots; i++) { ret = dw_mci_init_slot(host, i); @@ -3302,35 +3941,35 @@ EXPORT_SYMBOL(dw_mci_probe); void dw_mci_remove(struct dw_mci *host) { int i; + del_timer_sync(&host->dto_timer); - mci_writel(host, RINTSTS, 0xFFFFFFFF); - mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ + mci_writel(host, RINTSTS, 0xFFFFFFFF); + mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ - for (i = 0; i < host->num_slots; i++) { - dev_dbg(host->dev, "remove slot %d\n", i); - if (host->slot[i]) - dw_mci_cleanup_slot(host->slot[i], i); - } + for(i = 0; i < host->num_slots; i++){ + dev_dbg(host->dev, "remove slot %d\n", i); + if(host->slot[i]) + dw_mci_cleanup_slot(host->slot[i], i); + } - /* disable clock to CIU */ - mci_writel(host, CLKENA, 0); - mci_writel(host, CLKSRC, 0); + /* disable clock to CIU */ + mci_writel(host, CLKENA, 0); + mci_writel(host, CLKSRC, 0); - destroy_workqueue(host->card_workqueue); + destroy_workqueue(host->card_workqueue); - if (host->use_dma && host->dma_ops->exit) - host->dma_ops->exit(host); + if(host->use_dma && host->dma_ops->exit) + host->dma_ops->exit(host); - if (host->vmmc){ - regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } - - if (!IS_ERR(host->clk_mmc)) - clk_disable_unprepare(host->clk_mmc); + if(host->vmmc){ + regulator_disable(host->vmmc); + regulator_put(host->vmmc); + } + if(!IS_ERR(host->clk_mmc)) + clk_disable_unprepare(host->clk_mmc); - if (!IS_ERR(host->hclk_mmc)) - clk_disable_unprepare(host->hclk_mmc); + if(!IS_ERR(host->hclk_mmc)) + clk_disable_unprepare(host->hclk_mmc); } EXPORT_SYMBOL(dw_mci_remove); @@ -3342,34 +3981,84 @@ EXPORT_SYMBOL(dw_mci_remove); */ int dw_mci_suspend(struct dw_mci *host) { - - if (host->vmmc) - regulator_disable(host->vmmc); - - return 0; + if(host->vmmc) + regulator_disable(host->vmmc); + + if(host->use_dma && host->dma_ops->exit) + host->dma_ops->exit(host); + + /*only for sdmmc controller*/ + if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) { + host->mmc->rescan_disable = 1; + if (cancel_delayed_work_sync(&host->mmc->detect)) + wake_unlock(&host->mmc->detect_wake_lock); + + disable_irq(host->irq); + if (pinctrl_select_state(host->pinctrl, host->pins_idle) < 0) + MMC_DBG_ERR_FUNC(host->mmc, "Idle pinctrl setting failed! [%s]", + mmc_hostname(host->mmc)); + + mci_writel(host, RINTSTS, 0xFFFFFFFF); + mci_writel(host, INTMASK, 0x00); + mci_writel(host, CTRL, 0x00); + + /* Soc rk3126 already in gpio_cd mode */ + if (!(cpu_is_rk312x() && soc_is_rk3126())) { + dw_mci_of_get_cd_gpio(host->dev, 0, host->mmc); + enable_irq_wake(host->mmc->slot.cd_irq); + } + } + return 0; } EXPORT_SYMBOL(dw_mci_suspend); int dw_mci_resume(struct dw_mci *host) { - int i, ret; + int i, ret, retry_cnt = 0; u32 regs; + struct dw_mci_slot *slot; + + if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO){ + slot = mmc_priv(host->mmc); - if (host->vmmc) { + if(!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) + return 0; + } + /*only for sdmmc controller*/ + if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) { + /* Soc rk3126 already in gpio_cd mode */ + if (!(cpu_is_rk312x() && soc_is_rk3126())) { + disable_irq_wake(host->mmc->slot.cd_irq); + mmc_gpio_free_cd(host->mmc); + } + if(pinctrl_select_state(host->pinctrl, host->pins_default) < 0) + MMC_DBG_ERR_FUNC(host->mmc, "Default pinctrl setting failed! [%s]", + mmc_hostname(host->mmc)); + host->mmc->rescan_disable = 0; + /* Disable jtag*/ + if(cpu_is_rk3288()) + grf_writel(((1 << 12) << 16) | (0 << 12), RK3288_GRF_SOC_CON0); + else if(cpu_is_rk3036()) + grf_writel(((1 << 11) << 16) | (0 << 11), RK3036_GRF_SOC_CON0); + else if(cpu_is_rk312x()) + /* RK3036_GRF_SOC_CON0 is compatible with rk312x, tmp setting */ + grf_writel(((1 << 8) << 16) | (0 << 8), RK3036_GRF_SOC_CON0); + } + if(host->vmmc){ ret = regulator_enable(host->vmmc); - if (ret) { + if (ret){ dev_err(host->dev, "failed to enable regulator: %d\n", ret); return ret; } } - - if (!dw_mci_ctrl_all_reset(host)) { + + if(!dw_mci_ctrl_all_reset(host)){ ret = -ENODEV; return ret; } - if (host->use_dma && host->dma_ops->init) + if(host->use_dma && host->dma_ops->init) host->dma_ops->init(host); /* @@ -3388,16 +4077,21 @@ int dw_mci_resume(struct dw_mci *host) regs |= SDMMC_INT_CD; mci_writel(host, INTMASK, regs); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); + /*only for sdmmc controller*/ + if((host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)&& (!retry_cnt)){ + enable_irq(host->irq); + } - for (i = 0; i < host->num_slots; i++) { + for(i = 0; i < host->num_slots; i++){ struct dw_mci_slot *slot = host->slot[i]; - if (!slot) + if(!slot) continue; - if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + if(slot->mmc->pm_flags & MMC_PM_KEEP_POWER){ dw_mci_set_ios(slot->mmc, &slot->mmc->ios); dw_mci_setup_bus(slot, true); } } + return 0; } EXPORT_SYMBOL(dw_mci_resume); @@ -3416,10 +4110,9 @@ static void __exit dw_mci_exit(void) module_init(dw_mci_init); module_exit(dw_mci_exit); -MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); - +MODULE_DESCRIPTION("Rockchip specific DW Multimedia Card Interface driver"); MODULE_AUTHOR("NXP Semiconductor VietNam"); MODULE_AUTHOR("Imagination Technologies Ltd"); +MODULE_AUTHOR("Shawn Lin "); MODULE_AUTHOR("Rockchip Electronics£¬Bangwang Xie < xbw@rock-chips.com> "); - MODULE_LICENSE("GPL v2");