#include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/gpio.h>
 
 #include <linux/mmc/host.h>
 
        struct resource         *ioarea;
        struct s3c_sdhci_platdata *pdata;
        unsigned int            cur_clk;
+       int                     ext_cd_irq;
+       int                     ext_cd_gpio;
 
        struct clk              *clk_io;
        struct clk              *clk_bus[MAX_BUS_CLK];
        .get_min_clock          = sdhci_s3c_get_min_clock,
 };
 
+static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
+{
+       struct sdhci_host *host = platform_get_drvdata(dev);
+       if (host) {
+               mutex_lock(&host->lock);
+               if (state) {
+                       dev_dbg(&dev->dev, "card inserted.\n");
+                       host->flags &= ~SDHCI_DEVICE_DEAD;
+                       host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+               } else {
+                       dev_dbg(&dev->dev, "card removed.\n");
+                       host->flags |= SDHCI_DEVICE_DEAD;
+                       host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+               }
+               sdhci_card_detect(host);
+               mutex_unlock(&host->lock);
+       }
+}
+
+static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id)
+{
+       struct sdhci_s3c *sc = dev_id;
+       int status = gpio_get_value(sc->ext_cd_gpio);
+       if (sc->pdata->ext_cd_gpio_invert)
+               status = !status;
+       sdhci_s3c_notify_change(sc->pdev, status);
+       return IRQ_HANDLED;
+}
+
+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;
+               sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
+               if (sc->ext_cd_irq &&
+                   request_threaded_irq(sc->ext_cd_irq, NULL,
+                                        sdhci_s3c_gpio_card_detect_thread,
+                                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                                        dev_name(dev), sc) == 0) {
+                       int status = gpio_get_value(sc->ext_cd_gpio);
+                       if (pdata->ext_cd_gpio_invert)
+                               status = !status;
+                       sdhci_s3c_notify_change(sc->pdev, status);
+               } else {
+                       dev_warn(dev, "cannot request irq for card detect\n");
+                       sc->ext_cd_irq = 0;
+               }
+       } else {
+               dev_err(dev, "cannot request gpio for card detect\n");
+       }
+}
+
 static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
 {
        struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
        sc->host = host;
        sc->pdev = pdev;
        sc->pdata = pdata;
+       sc->ext_cd_gpio = -1; /* invalid gpio number */
 
        platform_set_drvdata(pdev, host);
 
         * SDHCI block, or a missing configuration that needs to be set. */
        host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ;
 
+       if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
+           pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+               host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
+       if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+               host->mmc->caps = MMC_CAP_NONREMOVABLE;
+
        host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
                         SDHCI_QUIRK_32BIT_DMA_SIZE);
 
                goto err_add_host;
        }
 
+       /* 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)
+               pdata->ext_cd_init(&sdhci_s3c_notify_change);
+       if (pdata->cd_type == S3C_SDHCI_CD_GPIO &&
+           gpio_is_valid(pdata->ext_cd_gpio))
+               sdhci_s3c_setup_card_detect_gpio(sc);
+
        return 0;
 
  err_add_host:
 
 static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
 {
+       struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
        struct sdhci_host *host =  platform_get_drvdata(pdev);
        struct sdhci_s3c *sc = sdhci_priv(host);
        int ptr;
 
+       if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
+               pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);
+
+       if (sc->ext_cd_irq)
+               free_irq(sc->ext_cd_irq, sc);
+
+       if (gpio_is_valid(sc->ext_cd_gpio))
+               gpio_free(sc->ext_cd_gpio);
+
        sdhci_remove_host(host, 1);
 
        for (ptr = 0; ptr < 3; ptr++) {