SDMMC: fix pad can not power down with sdmmc card insert

This commit is contained in:
xjh
2014-06-05 19:30:22 +08:00
parent 0b7a7ff4c8
commit 74698c2a61

View File

@@ -41,6 +41,7 @@
#include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/clk-private.h>
#include <linux/rockchip/cpu.h>
#include "rk_sdmmc.h"
#include "rk_sdmmc_of.h"
@@ -49,7 +50,7 @@
#define grf_writel(v, offset) do { writel_relaxed(v, RK_GRF_VIRT + offset); dsb(); } while (0)
#define RK_SDMMC_DRIVER_VERSION "Ver 1.10 2014-06-05"
#define RK_SDMMC_DRIVER_VERSION "Ver 1.11 2014-06-05"
/* Common flag combinations */
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
@@ -832,7 +833,7 @@ 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)
static int 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(500);//msecs_to_jiffies(5000);
@@ -854,32 +855,39 @@ 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 0;
}
dev_err(&slot->mmc->class_dev,
"Timeout sending command (cmd %#x arg %#x status %#x)\n",
cmd, arg, cmd_status);
return -EAGAIN;
}
static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
static int dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
{
struct dw_mci *host = slot->host;
unsigned int tempck,clock = slot->clock;
u32 div;
u32 clk_en_a;
u32 sdio_int;
int ret = 0;
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,
ret = mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
if(ret < 0)
return -EAGAIN;
} else if (clock != host->current_speed || force_clkinit) {
div = host->bus_hz / clock;
if (host->bus_hz % clock && host->bus_hz > clock)
@@ -907,7 +915,10 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKSRC, 0);
/* inform CIU */
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
ret = mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
if(ret < 0)
return -EAGAIN;
if (!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC))
goto normal;
@@ -971,8 +982,10 @@ normal:
mci_writel(host, CLKDIV, div);
/* inform CIU */
mci_send_cmd(slot,
ret = mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
if(ret < 0)
return -EAGAIN;
/* enable clock; only low power if no SDIO */
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
@@ -987,9 +1000,10 @@ normal:
mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */
mci_send_cmd(slot,
ret = mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
if(ret < 0)
return -EAGAIN;
/* keep the clock with reflecting clock dividor */
slot->__clk_old = clock << div;
}
@@ -1005,7 +1019,7 @@ normal:
/* Set the current slot bus width */
mci_writel(host, CTYPE, (slot->ctype << slot->id));
return ret;
}
static void dw_mci_wait_unbusy(struct dw_mci *host)
@@ -1181,7 +1195,7 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_bh(&host->lock);
}
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
@@ -1205,8 +1219,8 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
printk("slot->flags=%d ", slot->flags);
dump_stack();
printk("%d..%s: wait for unbusy timeout.......[%s]\n", \
__LINE__, __FUNCTION__, mmc_hostname(mmc));
printk("%d..%s: wait for unbusy timeout....... STATUS = 0x%x[%s]\n", \
__LINE__, __FUNCTION__, regs, mmc_hostname(mmc));
}
#endif
switch (ios->bus_width) {
@@ -1243,7 +1257,8 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
drv_data->set_ios(slot->host, ios);
/* Slot specific timing and width adjustment */
dw_mci_setup_bus(slot, false);
if(dw_mci_setup_bus(slot, false) < 0)
return -EAGAIN;
EXIT_POWER:
switch (ios->power_mode) {
@@ -1267,6 +1282,7 @@ EXIT_POWER:
default:
break;
}
return 0;
}
static int dw_mci_get_ro(struct mmc_host *mmc)
@@ -2504,13 +2520,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
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);
}
@@ -3501,13 +3519,18 @@ int dw_mci_suspend(struct dw_mci *host)
/*only for sdmmc controller*/
if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
disable_irq(host->irq);
if(pinctrl_select_state(host->pinctrl, host->pins_idle) < 0)
printk("%s: Warning : Idle pinctrl setting failed!\n", mmc_hostname(host->mmc));
dw_mci_of_get_cd_gpio(host->dev,0,host->mmc);
enable_irq_wake(host->mmc->slot.cd_irq);
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)
printk("%s: Warning : Idle pinctrl setting failed!\n", mmc_hostname(host->mmc));
dw_mci_of_get_cd_gpio(host->dev,0,host->mmc);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, 0x00);
mci_writel(host, CTRL, 0x00);
enable_irq_wake(host->mmc->slot.cd_irq);
}
return 0;
}
@@ -3515,7 +3538,7 @@ 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;
@@ -3525,7 +3548,17 @@ int dw_mci_resume(struct dw_mci *host)
if(!test_bit(DW_MMC_CARD_PRESENT, &slot->flags))
return 0;
}
/*only for sdmmc controller*/
if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
disable_irq_wake(host->mmc->slot.cd_irq);
mmc_gpio_free_cd(host->mmc);
if(pinctrl_select_state(host->pinctrl, host->pins_default) < 0)
printk("%s: Warning : Default pinctrl setting failed!\n", mmc_hostname(host->mmc));
host->mmc->rescan_disable = 0;
if(cpu_is_rk3288()) {
grf_writel(((1<<12)<<16)|(0<<12), RK3288_GRF_SOC_CON0);//disable jtag
}
}
if (host->vmmc) {
ret = regulator_enable(host->vmmc);
if (ret) {
@@ -3534,7 +3567,8 @@ int dw_mci_resume(struct dw_mci *host)
return ret;
}
}
RESUME_AGAIN:
if (!dw_mci_ctrl_all_reset(host)) {
ret = -ENODEV;
return ret;
@@ -3559,29 +3593,31 @@ 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);
}
if((retry_cnt++) > 0x100) {
dev_err(host->dev,
"failed to resume controller: %d.[%s]\n", ret, mmc_hostname(host->mmc));
return -ENODEV;
}
if(retry_cnt > 1)
dev_err("%s sdmmc resume error.[%s]\n",__func__, mmc_hostname(host->mmc));
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);
if(dw_mci_set_ios(slot->mmc, &slot->mmc->ios) < 0)
goto RESUME_AGAIN;
if(dw_mci_setup_bus(slot, true) < 0)
goto RESUME_AGAIN;
}
}
/*only for sdmmc controller*/
if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
disable_irq_wake(host->mmc->slot.cd_irq);
mmc_gpio_free_cd(host->mmc);
if(pinctrl_select_state(host->pinctrl, host->pins_default) < 0)
printk("%s: Warning : Default pinctrl setting failed!\n", mmc_hostname(host->mmc));
enable_irq(host->irq);
}
return 0;
}
EXPORT_SYMBOL(dw_mci_resume);