diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
-rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 215 |
1 files changed, 196 insertions, 19 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 4a5c501..712c8c6 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -24,6 +24,7 @@ #include <plat/sdhci.h> #include <plat/regs-sdhci.h> +#include <plat/gpio-cfg.h> #include "sdhci.h" @@ -47,6 +48,7 @@ struct sdhci_s3c { 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]; @@ -212,6 +214,12 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) if (ourhost->pdata->cfg_card) (ourhost->pdata->cfg_card)(ourhost->pdev, host->ioaddr, &ios, NULL); +#ifdef CONFIG_MACH_MIDAS + /* call cfg_gpio with 4bit data bus */ + if (ourhost->pdata->cfg_gpio) + ourhost->pdata->cfg_gpio(ourhost->pdev, 4); + +#endif } } @@ -288,6 +296,7 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width) { u8 ctrl; + struct sdhci_s3c *ourhost = to_s3c(host); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); @@ -295,14 +304,23 @@ static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width) case MMC_BUS_WIDTH_8: ctrl |= SDHCI_CTRL_8BITBUS; ctrl &= ~SDHCI_CTRL_4BITBUS; + /* call cfg_gpio with 8bit data bus */ + if (ourhost->pdata->cfg_gpio) + ourhost->pdata->cfg_gpio(ourhost->pdev, 8); break; case MMC_BUS_WIDTH_4: ctrl |= SDHCI_CTRL_4BITBUS; ctrl &= ~SDHCI_CTRL_8BITBUS; + /* call cfg_gpio with 4bit data bus */ + if (ourhost->pdata->cfg_gpio) + ourhost->pdata->cfg_gpio(ourhost->pdev, 4); break; default: - ctrl &= ~SDHCI_CTRL_4BITBUS; ctrl &= ~SDHCI_CTRL_8BITBUS; + ctrl &= ~SDHCI_CTRL_4BITBUS; + /* call cfg_gpio with 1bit data bus */ + if (ourhost->pdata->cfg_gpio) + ourhost->pdata->cfg_gpio(ourhost->pdev, 1); break; } @@ -311,11 +329,49 @@ static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width) return 0; } +#ifdef CONFIG_MIDAS_COMMON +/* midas board control the vdd for tflash by gpio, + not regulator directly. + so, code related vdd control should be added */ +static void sdhci_s3c_vtf_on_off(int on_off) +{ +#ifdef CONFIG_MIDAS_COMMON + int gpio = GPIO_TF_EN; +#else + int gpio = EXYNOS4212_GPJ0(7); +#endif + + if (on_off) { + gpio_set_value(gpio, 1); + } else { + gpio_set_value(gpio, 0); + } +} + + +static int sdhci_s3c_get_card_exist(struct sdhci_host *host) +{ + struct sdhci_s3c *sc; + int status; + + sc = sdhci_priv(host); + + status = gpio_get_value(sc->ext_cd_gpio); + if (sc->pdata->ext_cd_gpio_invert) + status = !status; + + return status; +} +#endif + static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, .set_clock = sdhci_s3c_set_clock, .get_min_clock = sdhci_s3c_get_min_clock, .platform_8bit_width = sdhci_s3c_platform_8bit_width, +#ifdef CONFIG_MIDAS_COMMON + .set_power = sdhci_s3c_vtf_on_off, +#endif }; static void sdhci_s3c_notify_change(struct platform_device *dev, int state) @@ -327,11 +383,13 @@ static void sdhci_s3c_notify_change(struct platform_device *dev, int state) spin_lock_irqsave(&host->lock, flags); if (state) { dev_dbg(&dev->dev, "card inserted.\n"); - host->flags &= ~SDHCI_DEVICE_DEAD; + pr_info("%s: card inserted.\n", + mmc_hostname(host->mmc)); host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; } else { dev_dbg(&dev->dev, "card removed.\n"); - host->flags |= SDHCI_DEVICE_DEAD; + pr_info("%s: card removed.\n", + mmc_hostname(host->mmc)); host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; } tasklet_schedule(&host->card_tasklet); @@ -345,6 +403,17 @@ static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) int status = gpio_get_value(sc->ext_cd_gpio); if (sc->pdata->ext_cd_gpio_invert) status = !status; + + if (sc->host->mmc) { + if (status) + mmc_host_sd_set_present(sc->host->mmc); + else + mmc_host_sd_clear_present(sc->host->mmc); + + pr_debug("SDcard present state=%d.\n", + mmc_host_sd_present(sc->host->mmc)); + } + sdhci_s3c_notify_change(sc->pdev, status); return IRQ_HANDLED; } @@ -354,8 +423,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) struct s3c_sdhci_platdata *pdata = sc->pdata; struct device *dev = &sc->pdev->dev; - if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { - sc->ext_cd_gpio = pdata->ext_cd_gpio; + if (sc->ext_cd_gpio > 0) { sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); if (sc->ext_cd_irq && request_threaded_irq(sc->ext_cd_irq, NULL, @@ -365,16 +433,56 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) int status = gpio_get_value(sc->ext_cd_gpio); if (pdata->ext_cd_gpio_invert) status = !status; + + if (status) + mmc_host_sd_set_present(sc->host->mmc); + else + mmc_host_sd_clear_present(sc->host->mmc); + + /* T-Flash EINT for CD SHOULD be wakeup source */ + irq_set_irq_wake(sc->ext_cd_irq, 1); + sdhci_s3c_notify_change(sc->pdev, status); } else { dev_warn(dev, "cannot request irq for card detect\n"); sc->ext_cd_irq = 0; } + } +} + +extern struct class *sec_class; +static struct device *sd_detection_cmd_dev; + +static ssize_t sd_detection_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sdhci_s3c *sc = dev_get_drvdata(dev); + unsigned int detect; + + if (sc && sc->ext_cd_gpio) + detect = gpio_get_value(sc->ext_cd_gpio); + else { + pr_info("%s : External SD detect pin Error\n", __func__); + return sprintf(buf, "Error\n"); + } + + if (sc->pdata->ext_cd_gpio_invert) { + pr_info("%s : Invert External SD detect pin\n", __func__); + detect = !detect; + } + + pr_info("%s : detect = %d.\n", __func__, detect); + if (detect) { + pr_debug("sdhci: card inserted.\n"); + return sprintf(buf, "Insert\n"); } else { - dev_err(dev, "cannot request gpio for card detect\n"); + pr_debug("sdhci: card removed.\n"); + return sprintf(buf, "Remove\n"); } } +static DEVICE_ATTR(status, 0444, sd_detection_cmd_show, NULL); + static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; @@ -472,7 +580,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (!host->ioaddr) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; - goto err_req_regs; + goto err_add_host; } /* Ensure we have minimal gpio selected CMD/CLK/Detect */ @@ -501,11 +609,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; - /* This host supports the Auto CMD12 */ - host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; - - if (pdata->cd_type == S3C_SDHCI_CD_NONE || - pdata->cd_type == S3C_SDHCI_CD_PERMANENT) + if (pdata->cd_type == S3C_SDHCI_CD_NONE) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) @@ -514,6 +618,10 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; + /* if vmmc_name is in pdata */ + if (pdata->vmmc_name) + host->vmmc_name = pdata->vmmc_name; + host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); @@ -534,17 +642,70 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; + /* for BCM WIFI */ + if (pdata->pm_flags) + host->mmc->pm_flags |= pdata->pm_flags; + + /* To turn on vmmc regulator only if sd card exists, + GPIO pin for card detection should be initialized. + Moved from sdhci_s3c_setup_card_detect_gpio() function */ + if (pdata->cd_type == S3C_SDHCI_CD_GPIO && + gpio_is_valid(pdata->ext_cd_gpio)) { + if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { + sc->ext_cd_gpio = pdata->ext_cd_gpio; + sc->ext_cd_gpio_invert = pdata->ext_cd_gpio_invert; + + mmc_host_sd_set_present(host->mmc); + if (sd_detection_cmd_dev == NULL && + sc->ext_cd_gpio) { + sd_detection_cmd_dev = + device_create(sec_class, NULL, 0, + NULL, "sdcard"); + if (IS_ERR(sd_detection_cmd_dev)) + pr_err("Fail to create sysfs dev\n"); + + if (device_create_file(sd_detection_cmd_dev, + &dev_attr_status) < 0) + pr_err("Fail to create sysfs file\n"); + + dev_set_drvdata(sd_detection_cmd_dev, sc); + } +#ifdef CONFIG_MIDAS_COMMON + /* set TF_EN gpio as OUTPUT */ + gpio_request(GPIO_TF_EN, "TF_EN"); + gpio_direction_output(GPIO_TF_EN, 1); + s3c_gpio_cfgpin(GPIO_TF_EN, S3C_GPIO_SFN(1)); + s3c_gpio_setpull(GPIO_TF_EN, S3C_GPIO_PULL_NONE); +#endif + } else { + dev_err(dev, "cannot request gpio for card detect\n"); + } + } + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } + /* if it is set SDHCI_QUIRK_BROKEN_CARD_DETECTION before calling + sdhci_add_host, in sdhci_add_host, MMC_CAP_NEEDS_POLL flag will + be set. The flag S3C_SDHCI_CD_PERMANENT dose not need to + detect a card by polling. */ + if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT || \ + pdata->cd_type == S3C_SDHCI_CD_GPIO) + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + /* The following two methods of card detection might call sdhci_s3c_notify_change() immediately, so they can be called only after sdhci_add_host(). Setup errors are ignored. */ - if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) + if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) { pdata->ext_cd_init(&sdhci_s3c_notify_change); +#ifdef CONFIG_MACH_PX + if (pdata->ext_pdev) + pdata->ext_pdev(pdev); +#endif + } if (pdata->cd_type == S3C_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) sdhci_s3c_setup_card_detect_gpio(sc); @@ -552,8 +713,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) return 0; err_add_host: - release_resource(sc->ioarea); - kfree(sc->ioarea); + if (host->ioaddr) + iounmap(host->ioaddr); + release_mem_region(sc->ioarea->start, resource_size(sc->ioarea)); err_req_regs: for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { @@ -613,17 +775,27 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev) static int sdhci_s3c_suspend(struct platform_device *dev, pm_message_t pm) { struct sdhci_host *host = platform_get_drvdata(dev); + int ret = 0; - sdhci_suspend_host(host, pm); - return 0; + ret = sdhci_suspend_host(host, pm); + + return ret; +} + +static void sdhci_s3c_shutdown(struct platform_device *dev) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + + sdhci_shutdown_host(host); } static int sdhci_s3c_resume(struct platform_device *dev) { struct sdhci_host *host = platform_get_drvdata(dev); + int ret = 0; - sdhci_resume_host(host); - return 0; + ret = sdhci_resume_host(host); + return ret; } #else @@ -636,6 +808,7 @@ static struct platform_driver sdhci_s3c_driver = { .remove = __devexit_p(sdhci_s3c_remove), .suspend = sdhci_s3c_suspend, .resume = sdhci_s3c_resume, + .shutdown = sdhci_s3c_shutdown, .driver = { .owner = THIS_MODULE, .name = "s3c-sdhci", @@ -652,7 +825,11 @@ static void __exit sdhci_s3c_exit(void) platform_driver_unregister(&sdhci_s3c_driver); } +#ifdef CONFIG_FAST_RESUME +beforeresume_initcall(sdhci_s3c_init); +#else module_init(sdhci_s3c_init); +#endif module_exit(sdhci_s3c_exit); MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue"); |