diff options
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 233 |
1 files changed, 191 insertions, 42 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6d3de08..9f23876 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -25,13 +25,16 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/host.h> +#include <linux/mmc/card.h> #include "sdhci.h" +#include <linux/gpio.h> + #define DRIVER_NAME "sdhci" #define DBG(f, x...) \ - pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x) #if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \ defined(CONFIG_MMC_SDHCI_MODULE)) @@ -46,9 +49,25 @@ static void sdhci_finish_data(struct sdhci_host *); static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); static void sdhci_finish_command(struct sdhci_host *); -static int sdhci_execute_tuning(struct mmc_host *mmc); +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_tuning_timer(unsigned long data); +#define MAX_BUS_CLK (4) + +struct sdhci_s3c { + struct sdhci_host *host; + struct platform_device *pdev; + struct resource *ioarea; + struct s3c_sdhci_platdata *pdata; + unsigned int cur_clk; + int ext_cd_irq; + int ext_cd_gpio; + int ext_cd_gpio_invert; + + struct clk *clk_io; + struct clk *clk_bus[MAX_BUS_CLK]; +}; + static void sdhci_dumpregs(struct sdhci_host *host) { printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", @@ -651,6 +670,17 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) break; } + /* card's type is SD, set timeout */ + if (host->mmc->card && mmc_card_sd(host->mmc->card)) { + count += 2; + /* + * It's to prevent warning error log, + * If count value is more than 0xD before add 2. + */ + if (count >= 0xF) + count = 0xE; + } + if (count >= 0xF) { printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n", mmc_hostname(host->mmc), cmd->opcode); @@ -1006,7 +1036,7 @@ static void sdhci_finish_command(struct sdhci_host *host) if (host->cmd->flags & MMC_RSP_PRESENT) { if (host->cmd->flags & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ - for (i = 0;i < 4;i++) { + for (i = 0 ; i < 4 ; i++) { host->cmd->resp[i] = sdhci_readl(host, SDHCI_RESPONSE + (3-i)*4) << 8; if (i != 3) @@ -1044,7 +1074,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk = 0; unsigned long timeout; - if (clock == host->clock) + if (clock && clock == host->clock) return; if (host->ops->set_clock) { @@ -1250,13 +1280,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) if ((host->flags & SDHCI_NEEDS_RETUNING) && !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { spin_unlock_irqrestore(&host->lock, flags); - sdhci_execute_tuning(mmc); + sdhci_execute_tuning(mmc, mrq->cmd->opcode); spin_lock_irqsave(&host->lock, flags); /* Restore original mmc_request structure */ host->mrq = mrq; } - if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) sdhci_send_command(host, mrq->sbc); else @@ -1275,10 +1304,41 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host = mmc_priv(mmc); + if ((!mmc_host_sd_present(mmc) || + (mmc_host_sd_present(mmc) && + !mmc_host_sd_init_stat(mmc) && + mmc_host_sd_prev_stat(mmc))) && + ios->power_mode == MMC_POWER_OFF) { + mmc_host_sd_clear_prev_stat(mmc); + if (host->vmmc && regulator_is_enabled(host->vmmc)) { +#ifdef CONFIG_MIDAS_COMMON + if (host->ops->set_power) + host->ops->set_power(0); +#endif + regulator_disable(host->vmmc); + pr_info("%s : MMC Card OFF %s\n", __func__, + host->hw_name); + } + } else if (mmc_host_sd_present(mmc) && + !mmc_host_sd_prev_stat(mmc)) { + mmc_host_sd_set_prev_stat(mmc); + if (host->vmmc && !regulator_is_enabled(host->vmmc)) { +#ifdef CONFIG_MIDAS_COMMON + if (host->ops->set_power) + host->ops->set_power(1); +#endif + regulator_enable(host->vmmc); + pr_info("%s : MMC Card ON %s\n", __func__, + host->hw_name); + } + } + spin_lock_irqsave(&host->lock, flags); - if (host->flags & SDHCI_DEVICE_DEAD) + if (host->flags & SDHCI_DEVICE_DEAD) { + sdhci_set_clock(host, 0); goto out; + } /* * Reset the chip on each power off. @@ -1416,7 +1476,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * signalling timeout and CRC errors even on CMD0. Resetting * it on each ios seems to solve the problem. */ - if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) + if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); out: @@ -1469,6 +1529,14 @@ static int sdhci_get_ro(struct mmc_host *mmc) return 0; } +static void sdhci_hw_reset(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops && host->ops->hw_reset) + host->ops->hw_reset(host); +} + static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct sdhci_host *host; @@ -1593,7 +1661,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return 0; } -static int sdhci_execute_tuning(struct mmc_host *mmc) +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host; u16 ctrl; @@ -1651,7 +1719,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) if (!tuning_loop_counter && !timeout) break; - cmd.opcode = MMC_SEND_TUNING_BLOCK; + cmd.opcode = opcode; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.retries = 0; @@ -1803,6 +1871,7 @@ static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .set_ios = sdhci_set_ios, .get_ro = sdhci_get_ro, + .hw_reset = sdhci_hw_reset, .enable_sdio_irq = sdhci_enable_sdio_irq, .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .execute_tuning = sdhci_execute_tuning, @@ -1820,16 +1889,16 @@ static void sdhci_tasklet_card(unsigned long param) struct sdhci_host *host; unsigned long flags; - host = (struct sdhci_host*)param; + host = (struct sdhci_host *)param; spin_lock_irqsave(&host->lock, flags); if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { if (host->mrq) { printk(KERN_ERR "%s: Card removed during transfer!\n", - mmc_hostname(host->mmc)); + mmc_hostname(host->mmc)); printk(KERN_ERR "%s: Resetting controller.\n", - mmc_hostname(host->mmc)); + mmc_hostname(host->mmc)); sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); @@ -1841,7 +1910,11 @@ static void sdhci_tasklet_card(unsigned long param) spin_unlock_irqrestore(&host->lock, flags); - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + if (host->vmmc && + !(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) + mmc_detect_change(host->mmc, msecs_to_jiffies(0)); + else + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); } static void sdhci_tasklet_finish(unsigned long param) @@ -1850,12 +1923,12 @@ static void sdhci_tasklet_finish(unsigned long param) unsigned long flags; struct mmc_request *mrq; - host = (struct sdhci_host*)param; + host = (struct sdhci_host *)param; - /* - * If this tasklet gets rescheduled while running, it will - * be run again afterwards but without any active request. - */ + /* + * If this tasklet gets rescheduled while running, it will + * be run again afterwards but without any active request. + */ if (!host->mrq) return; @@ -1910,7 +1983,7 @@ static void sdhci_timeout_timer(unsigned long data) struct sdhci_host *host; unsigned long flags; - host = (struct sdhci_host*)data; + host = (struct sdhci_host *)data; spin_lock_irqsave(&host->lock, flags); @@ -1968,11 +2041,20 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) return; } - if (intmask & SDHCI_INT_TIMEOUT) + if (intmask & SDHCI_INT_TIMEOUT) { + printk(KERN_INFO "%s: cmd %d command timeout error\n", + mmc_hostname(host->mmc), host->cmd->opcode); host->cmd->error = -ETIMEDOUT; - else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | - SDHCI_INT_INDEX)) + } else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | + SDHCI_INT_INDEX)) { + printk(KERN_ERR "%s: cmd %d %s error\n", + mmc_hostname(host->mmc), host->cmd->opcode, + (intmask & SDHCI_INT_CRC) ? "command crc" : + (intmask & SDHCI_INT_END_BIT) ? "command end bit" : + "command index error"); host->cmd->error = -EILSEQ; + } + if (host->cmd->error) { tasklet_schedule(&host->finish_tasklet); @@ -2069,15 +2151,17 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) return; } - if (intmask & SDHCI_INT_DATA_TIMEOUT) - host->data->error = -ETIMEDOUT; - else if (intmask & SDHCI_INT_DATA_END_BIT) - host->data->error = -EILSEQ; - else if ((intmask & SDHCI_INT_DATA_CRC) && - SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) - != MMC_BUS_TEST_R) + if (intmask & SDHCI_INT_DATA_TIMEOUT) { + printk(KERN_ERR "%s: cmd %d data timeout error\n", + mmc_hostname(host->mmc), host->mrq->cmd->opcode); + host->data->error = -ETIMEDOUT; + } else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) { + printk(KERN_ERR "%s: cmd %d %s error\n", + mmc_hostname(host->mmc), host->mrq->cmd->opcode, + (intmask & SDHCI_INT_DATA_CRC) ? "data crc" : + "command end bit"); host->data->error = -EILSEQ; - else if (intmask & SDHCI_INT_ADMA_ERROR) { + } else if (intmask & SDHCI_INT_ADMA_ERROR) { printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); sdhci_show_adma_error(host); host->data->error = -EIO; @@ -2134,7 +2218,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result; - struct sdhci_host* host = dev_id; + struct sdhci_host *host = dev_id; u32 intmask; int cardint = 0; @@ -2232,14 +2316,27 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) host->tuning_count * HZ); } + if (host->mmc->pm_flags & MMC_PM_IGNORE_SUSPEND_RESUME) { + host->mmc->pm_flags |= MMC_PM_KEEP_POWER; + pr_info("%s : Enter WIFI suspend\n", __func__); + } + ret = mmc_suspend_host(host->mmc); if (ret) return ret; free_irq(host->irq, host); - if (host->vmmc) - ret = regulator_disable(host->vmmc); + if (host->vmmc) { + if (regulator_is_enabled(host->vmmc)) { +#ifdef CONFIG_MIDAS_COMMON + if (host->ops->set_power) + host->ops->set_power(0); +#endif + ret = regulator_disable(host->vmmc); + pr_info("%s : MMC Card OFF\n", __func__); + } + } return ret; } @@ -2250,10 +2347,15 @@ int sdhci_resume_host(struct sdhci_host *host) { int ret; - if (host->vmmc) { - int ret = regulator_enable(host->vmmc); + if (host->vmmc && !regulator_is_enabled(host->vmmc)) { +#ifdef CONFIG_MIDAS_COMMON + if (host->ops->set_power) + host->ops->set_power(1); +#endif + ret = regulator_enable(host->vmmc); if (ret) return ret; + pr_info("%s : MMC Card ON\n", __func__); } @@ -2278,6 +2380,13 @@ int sdhci_resume_host(struct sdhci_host *host) (host->tuning_mode == SDHCI_TUNING_MODE_1)) host->flags |= SDHCI_NEEDS_RETUNING; +#ifdef CONFIG_MACH_PX + /* host has a card and the card is SDIO type */ + if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { + /* enable sdio interrupt */ + sdhci_enable_sdio_irq(host->mmc, 1); + } +#endif return ret; } @@ -2328,6 +2437,7 @@ int sdhci_add_host(struct sdhci_host *host) u32 max_current_caps; unsigned int ocr_avail; int ret; + struct sdhci_s3c *sc; WARN_ON(host == NULL); if (host == NULL) @@ -2485,7 +2595,12 @@ int sdhci_add_host(struct sdhci_host *host) } else mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; - mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; + if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) + mmc->max_discard_to = (1 << 27) / (mmc->f_max / 1000); + else + mmc->max_discard_to = (1 << 27) / host->timeout_clk; + + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; @@ -2542,6 +2657,15 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[1] & SDHCI_DRIVER_TYPE_D) mmc->caps |= MMC_CAP_DRIVER_TYPE_D; + /* + * If Power Off Notify capability is enabled by the host, + * set notify to short power off notify timeout value. + */ + if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY) + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; + else + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE; + /* Initial value for re-tuning timer count */ host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> SDHCI_RETUNING_TIMER_COUNT_SHIFT; @@ -2716,12 +2840,32 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) goto untasklet; - host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + sc = sdhci_priv(host); + + if (host->vmmc_name) + host->vmmc = regulator_get(mmc_dev(mmc), host->vmmc_name); + else + host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + if (IS_ERR(host->vmmc)) { - printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc)); + printk(KERN_ERR "%s: no %s regulator found\n", + mmc_hostname(mmc), + host->vmmc_name ? host->vmmc_name : "vmmc"); host->vmmc = NULL; } else { - regulator_enable(host->vmmc); + printk(KERN_INFO "%s: %s regulator found\n", + mmc_hostname(mmc), + host->vmmc_name ? host->vmmc_name : "vmmc"); + if (sc->ext_cd_gpio) { + if (gpio_get_value(sc->ext_cd_gpio) != (sc->ext_cd_gpio_invert)) { +#ifdef CONFIG_MIDAS_COMMON + if (host->ops->set_power) + host->ops->set_power(1); +#endif + regulator_enable(host->vmmc); + mdelay(100); + } + } } sdhci_init(host, 0); @@ -2810,7 +2954,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); - if (host->vmmc) { + if (host->vmmc && regulator_is_enabled(host->vmmc)) { +#ifdef CONFIG_MIDAS_COMMON + if (host->ops->set_power) + host->ops->set_power(0); +#endif regulator_disable(host->vmmc); regulator_put(host->vmmc); } @@ -2839,11 +2987,12 @@ EXPORT_SYMBOL_GPL(sdhci_free_host); static int __init sdhci_drv_init(void) { + int ret = 0; printk(KERN_INFO DRIVER_NAME ": Secure Digital Host Controller Interface driver\n"); printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - return 0; + return ret; } static void __exit sdhci_drv_exit(void) |