diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-11-12 15:28:39 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-11-12 15:28:39 +0100 |
commit | f987e832a9e79d2ce8009a5ea9c7b677624b3b30 (patch) | |
tree | 0dd09a5e6b4c60ee0a9916907dfc2cda83f3e496 /drivers/mmc | |
parent | f737b7f46a72c099cf8ac88baff02fbf61b1a47c (diff) | |
parent | fc993d9bc48f772133d8cd156c67c296477db070 (diff) |
Merge branch 'l4t/l4t-r16-r2' into colibri
Conflicts:
arch/arm/mach-tegra/tegra3_usb_phy.c
arch/arm/mach-tegra/usb_phy.c
drivers/usb/gadget/tegra_udc.c
drivers/usb/otg/Makefile
drivers/video/tegra/fb.c
sound/soc/tegra/tegra_pcm.c
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 26 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 19 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.c | 26 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.h | 3 | ||||
-rw-r--r-- | drivers/mmc/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-tegra.c | 123 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 38 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 1 |
8 files changed, 146 insertions, 91 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 35f3df8810e0..f7528db8fd06 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2109,6 +2109,26 @@ void mmc_stop_host(struct mmc_host *host) mmc_power_off(host); } +int mmc_speed_class_control(struct mmc_host *host, + unsigned int speed_class_ctrl_arg) +{ + int err = -ENOSYS; + u32 status; + + err = mmc_send_speed_class_ctrl(host, speed_class_ctrl_arg); + if (err) + return err; + + /* Issue CMD13 to check for any errors during the busy period of CMD20 */ + err = mmc_send_status(host->card, &status); + if (!err) { + if (status & R1_ERROR) + err = -EINVAL; + } + return err; +} +EXPORT_SYMBOL(mmc_speed_class_control); + int mmc_power_save_host(struct mmc_host *host) { int ret = 0; @@ -2163,6 +2183,9 @@ int mmc_card_awake(struct mmc_host *host) { int err = -ENOSYS; + if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) + return 0; + mmc_bus_get(host); if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) @@ -2178,6 +2201,9 @@ int mmc_card_sleep(struct mmc_host *host) { int err = -ENOSYS; + if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) + return 0; + mmc_bus_get(host); if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 40c93b3dccd7..0e8001facac3 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -976,13 +976,19 @@ static void mmc_detect(struct mmc_host *host) */ static int mmc_suspend(struct mmc_host *host) { + int err; + BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); - if (!mmc_host_is_spi(host)) + if (mmc_card_can_sleep(host)) { + err = mmc_card_sleep(host); + if (!err) + mmc_card_set_sleep(host->card); + } else if (!mmc_host_is_spi(host)) mmc_deselect_cards(host); - host->card->state &= ~MMC_STATE_HIGHSPEED; + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_release_host(host); return 0; @@ -1002,7 +1008,11 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - err = mmc_init_card(host, host->ocr, host->card); + if (mmc_card_is_sleep(host->card)) { + err = mmc_card_awake(host); + mmc_card_clr_sleep(host->card); + } else + err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; @@ -1012,7 +1022,8 @@ static int mmc_power_restore(struct mmc_host *host) { int ret; - host->card->state &= ~MMC_STATE_HIGHSPEED; + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + mmc_card_clr_sleep(host->card); mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 021fed153804..b06781e69ce3 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -389,3 +389,29 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) return 0; } + +int mmc_send_speed_class_ctrl(struct mmc_host *host, + unsigned int speed_class_ctrl_arg) +{ + int err = 0; + struct mmc_command cmd = { + .opcode = SD_SPEED_CLASS_CONTROL, + .arg = (speed_class_ctrl_arg << 28), + .flags = MMC_RSP_R1B | MMC_CMD_AC | MMC_RSP_BUSY, + }; + + BUG_ON(!host); + BUG_ON(speed_class_ctrl_arg > 3); + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); + if (err) + return err; + + /* + * If the host does not wait while the card signals busy, then we will + * will have to wait the max busy indication timeout. + */ + if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + mmc_delay(1000); + return err; +} + diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h index ffc2305d905f..a77b8facceb4 100644 --- a/drivers/mmc/core/sd_ops.h +++ b/drivers/mmc/core/sd_ops.h @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr); int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp); int mmc_app_sd_status(struct mmc_card *card, void *ssr); - +int mmc_send_speed_class_ctrl(struct mmc_host *host, + unsigned int speed_class_ctrl_arg); #endif diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f5ea51bd0ed3..2f9d6f4f43e1 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o +CFLAGS_sdhci-tegra.o = -Werror obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 609fd6391d1f..8ff35e4cbfe4 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -74,7 +74,9 @@ static unsigned int tegra_sdhost_std_freq; #ifdef CONFIG_ARCH_TEGRA_3x_SOC static void tegra_3x_sdhci_set_card_clock(struct sdhci_host *sdhci, unsigned int clock); static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci); +#endif +#ifndef CONFIG_ARCH_TEGRA_2x_SOC static unsigned int tegra3_sdhost_max_clk[4] = { 208000000, 104000000, 208000000, 104000000 }; #endif @@ -227,7 +229,7 @@ static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci) SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT; sdhci_writew(sdhci, misc_ctrl, SDHCI_VENDOR_MISC_CNTRL); } -#endif +#endif /* #ifdef CONFIG_ARCH_TEGRA_3x_SOC */ static int tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) @@ -372,14 +374,7 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci, unsigned int clk_rate; unsigned int emc_clk; - /* - * In SDR50 mode, run the sdmmc controller at freq greater than - * 104MHz to ensure the core voltage is at 1.2V. If the core voltage - * is below 1.2V, CRC errors would occur during data transfers. - */ - if (sdhci->mmc->card && - (mmc_card_ddr_mode(sdhci->mmc->card) || - (sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50))) { + if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_DDR50) { /* * In ddr mode, tegra sdmmc controller clock frequency * should be double the card clock frequency. @@ -394,6 +389,13 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci, } else { clk_rate = clock * 2; } + } else if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50) { + /* + * In SDR50 mode, run the sdmmc controller at freq greater than + * 104MHz to ensure the core voltage is at 1.2V. If the core voltage + * is below 1.2V, CRC errors would occur during data transfers. + */ + clk_rate = clock * 2; } else { if (clock <= tegra_sdhost_min_freq) clk_rate = tegra_sdhost_min_freq; @@ -410,7 +412,6 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci, clk_set_rate(pltfm_host->clk, clk_rate); sdhci->max_clk = clk_get_rate(pltfm_host->clk); } - #ifdef CONFIG_ARCH_TEGRA_3x_SOC static void tegra_3x_sdhci_set_card_clock(struct sdhci_host *sdhci, unsigned int clock) { @@ -496,7 +497,7 @@ set_clk: out: sdhci->clock = clock; } -#endif +#endif /* #ifdef CONFIG_ARCH_TEGRA_3x_SOC */ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock) { @@ -509,7 +510,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock) if (clock) { /* bring out sd instance from io dpd mode */ - tegra_io_dpd_disable(tegra_host->dpd); + if (tegra_host->dpd) { + mutex_lock(&tegra_host->dpd->delay_lock); + cancel_delayed_work_sync(&tegra_host->dpd->delay_dpd); + tegra_io_dpd_disable(tegra_host->dpd); + mutex_unlock(&tegra_host->dpd->delay_lock); + } if (!tegra_host->clk_enabled) { clk_enable(pltfm_host->clk); @@ -530,7 +536,18 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock) clk_disable(pltfm_host->clk); tegra_host->clk_enabled = false; /* io dpd enable call for sd instance */ - tegra_io_dpd_enable(tegra_host->dpd); + + if (tegra_host->dpd) { + mutex_lock(&tegra_host->dpd->delay_lock); + if (tegra_host->dpd->need_delay_dpd) { + schedule_delayed_work( + &tegra_host->dpd->delay_dpd, + msecs_to_jiffies(100)); + } else { + tegra_io_dpd_enable(tegra_host->dpd); + } + mutex_unlock(&tegra_host->dpd->delay_lock); + } } } @@ -658,36 +675,15 @@ static void sdhci_tegra_set_tap_delay(struct sdhci_host *sdhci, sdhci_writel(sdhci, vendor_ctrl, SDHCI_VENDOR_CLOCK_CNTRL); } -static void sdhci_tegra_clear_set_irqs(struct sdhci_host *host, - u32 clear, u32 set) -{ - u32 ier; - - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - ier &= ~clear; - ier |= set; - sdhci_writel(host, ier, SDHCI_INT_ENABLE); - sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); -} - static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci) { int err = 0; u8 ctrl; - u32 ier; u32 mask; unsigned int timeout = 10; int flags; u32 intstatus; - /* - * As per the Host Controller spec v3.00, tuning command - * generates Buffer Read Ready interrupt only, so enable that. - */ - ier = sdhci_readl(sdhci, SDHCI_INT_ENABLE); - sdhci_tegra_clear_set_irqs(sdhci, ier, SDHCI_INT_DATA_AVAIL | - SDHCI_INT_DATA_CRC); - mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; while (sdhci_readl(sdhci, SDHCI_PRESENT_STATE) & mask) { if (timeout == 0) { @@ -759,7 +755,6 @@ static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci) } mdelay(1); out: - sdhci_tegra_clear_set_irqs(sdhci, SDHCI_INT_DATA_AVAIL, ier); return err; } @@ -773,6 +768,7 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci) unsigned int temp_pass_window = 0; unsigned int best_low_pass_tap = 0; unsigned int best_pass_window = 0; + u32 ier; /* Tuning is valid only in SDR104 and SDR50 modes */ ctrl_2 = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2); @@ -785,11 +781,20 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci) if (tap_delay_status == NULL) { dev_err(mmc_dev(sdhci->mmc), "failed to allocate memory" "for storing tap_delay_status\n"); - err = -ENOMEM; - goto out; + return -ENOMEM; } /* + * Disable all interrupts signalling.Enable interrupt status + * detection for buffer read ready and data crc. We use + * polling for tuning as it involves less overhead. + */ + ier = sdhci_readl(sdhci, SDHCI_INT_ENABLE); + sdhci_writel(sdhci, 0, SDHCI_SIGNAL_ENABLE); + sdhci_writel(sdhci, SDHCI_INT_DATA_AVAIL | + SDHCI_INT_DATA_CRC, SDHCI_INT_ENABLE); + + /* * Set each tap delay value and run frequency tuning. After each * run, update the tap delay status as working or not working. */ @@ -840,7 +845,10 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci) /* Run frequency tuning */ err = sdhci_tegra_run_frequency_tuning(sdhci); -out: + /* Enable the normal interrupts signalling */ + sdhci_writel(sdhci, ier, SDHCI_INT_ENABLE); + sdhci_writel(sdhci, ier, SDHCI_SIGNAL_ENABLE); + if (tap_delay_status) kfree(tap_delay_status); @@ -865,6 +873,12 @@ static int tegra_sdhci_suspend(struct sdhci_host *sdhci, pm_message_t state) } } + if (tegra_host->dpd) { + mutex_lock(&tegra_host->dpd->delay_lock); + tegra_host->dpd->need_delay_dpd = 1; + mutex_unlock(&tegra_host->dpd->delay_lock); + } + return 0; } @@ -905,9 +919,6 @@ static struct sdhci_ops tegra_sdhci_ops = { .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, .platform_8bit_width = tegra_sdhci_8bit, -#ifdef CONFIG_ARCH_TEGRA_3x_SOC - .set_card_clock = tegra_3x_sdhci_set_card_clock, -#endif .set_clock = tegra_sdhci_set_clock, .suspend = tegra_sdhci_suspend, .resume = tegra_sdhci_resume, @@ -981,7 +992,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) "failed to allocate power gpio\n"); goto err_power_req; } - tegra_gpio_enable(plat->power_gpio); gpio_direction_output(plat->power_gpio, 1); } @@ -992,7 +1002,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) "failed to allocate cd gpio\n"); goto err_cd_req; } - tegra_gpio_enable(plat->cd_gpio); gpio_direction_input(plat->cd_gpio); tegra_host->card_present = (gpio_get_value(plat->cd_gpio) == 0); @@ -1027,7 +1036,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) "failed to allocate wp gpio\n"); goto err_wp_req; } - tegra_gpio_enable(plat->wp_gpio); gpio_direction_input(plat->wp_gpio); } @@ -1133,12 +1141,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_BKOPS; #endif -#ifdef CONFIG_MMC_EMBEDDED_SDIO - /* Do not turn OFF embedded sdio cards as it support Wake on Wireless */ - if (plat->mmc_data.embedded_sdio) - host->mmc->pm_flags |= MMC_PM_KEEP_POWER; -#endif - tegra_sdhost_min_freq = TEGRA_SDHOST_MIN_FREQ; #ifdef CONFIG_ARCH_TEGRA_2x_SOC tegra_host->hw_ops = &tegra_2x_sdhci_ops; @@ -1160,23 +1162,17 @@ err_add_host: err_clk_put: clk_put(pltfm_host->clk); err_clk_get: - if (gpio_is_valid(plat->wp_gpio)) { - tegra_gpio_disable(plat->wp_gpio); + if (gpio_is_valid(plat->wp_gpio)) gpio_free(plat->wp_gpio); - } err_wp_req: if (gpio_is_valid(plat->cd_gpio)) free_irq(gpio_to_irq(plat->cd_gpio), host); err_cd_irq_req: - if (gpio_is_valid(plat->cd_gpio)) { - tegra_gpio_disable(plat->cd_gpio); + if (gpio_is_valid(plat->cd_gpio)) gpio_free(plat->cd_gpio); - } err_cd_req: - if (gpio_is_valid(plat->power_gpio)) { - tegra_gpio_disable(plat->power_gpio); + if (gpio_is_valid(plat->power_gpio)) gpio_free(plat->power_gpio); - } err_power_req: err_no_mem: kfree(tegra_host); @@ -1209,21 +1205,16 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev) regulator_put(tegra_host->vdd_io_reg); } - if (gpio_is_valid(plat->wp_gpio)) { - tegra_gpio_disable(plat->wp_gpio); + if (gpio_is_valid(plat->wp_gpio)) gpio_free(plat->wp_gpio); - } if (gpio_is_valid(plat->cd_gpio)) { free_irq(gpio_to_irq(plat->cd_gpio), host); - tegra_gpio_disable(plat->cd_gpio); gpio_free(plat->cd_gpio); } - if (gpio_is_valid(plat->power_gpio)) { - tegra_gpio_disable(plat->power_gpio); + if (gpio_is_valid(plat->power_gpio)) gpio_free(plat->power_gpio); - } if (tegra_host->clk_enabled) clk_disable(pltfm_host->clk); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d3e9a4a4169f..aa03ca7d5226 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1852,19 +1852,15 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable) int sdhci_enable(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); + u16 clk; - if (!mmc->card) + if (!mmc->card || mmc->card->type == MMC_TYPE_SDIO) return 0; if (mmc->ios.clock) { - if (mmc->card->type != MMC_TYPE_SDIO) { - if (host->ops->set_clock) - host->ops->set_clock(host, mmc->ios.clock); - sdhci_set_clock(host, mmc->ios.clock); - } else { - if (host->ops->set_card_clock) - host->ops->set_card_clock(host, mmc->ios.clock); - } + if (host->ops->set_clock) + host->ops->set_clock(host, mmc->ios.clock); + sdhci_set_clock(host, mmc->ios.clock); } return 0; @@ -1873,19 +1869,14 @@ int sdhci_enable(struct mmc_host *mmc) int sdhci_disable(struct mmc_host *mmc, int lazy) { struct sdhci_host *host = mmc_priv(mmc); + u16 clk; - if (!mmc->card) + if (!mmc->card || mmc->card->type == MMC_TYPE_SDIO) return 0; - /* For SDIO cards, only disable the card clock. */ - if (mmc->card->type != MMC_TYPE_SDIO) { - sdhci_set_clock(host, 0); - if (host->ops->set_clock) - host->ops->set_clock(host, 0); - } else { - if (host->ops->set_card_clock) - host->ops->set_card_clock(host, 0); - } + sdhci_set_clock(host, 0); + if (host->ops->set_clock) + host->ops->set_clock(host, 0); return 0; } @@ -2348,6 +2339,15 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) } if (mmc->card) { + /* + * If eMMC cards are put in sleep state, Vccq can be disabled + * but Vcc would still be powered on. In resume, we only restore + * the controller context. So, set MMC_PM_KEEP_POWER flag. + */ + if (mmc_card_can_sleep(mmc) && + !(mmc->caps & MMC_CAP2_NO_SLEEP_CMD)) + mmc->pm_flags = MMC_PM_KEEP_POWER; + ret = mmc_suspend_host(host->mmc); if (ret) { if (has_tuning_timer) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index bf48767e0ef2..c00833de19da 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -260,7 +260,6 @@ struct sdhci_ops { #endif void (*set_clock)(struct sdhci_host *host, unsigned int clock); - void (*set_card_clock)(struct sdhci_host *host, unsigned int clock); int (*enable_dma)(struct sdhci_host *host); unsigned int (*get_max_clock)(struct sdhci_host *host); |