aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-s3c.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
-rw-r--r--drivers/mmc/host/sdhci-s3c.c215
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");