mmc: remove the "state" argument to mmc_suspend_host()
[safe/jmp/linux-2.6] / drivers / mmc / host / s3cmci.c
index 2af6306..2e16e0a 100644 (file)
@@ -3,6 +3,9 @@
  *
  *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
  *
+ * Current driver maintained by Ben Dooks and Simtec Electronics
+ *  Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 
-#include <asm/dma.h>
+#include <mach/dma.h>
 
 #include <mach/regs-sdi.h>
 #include <mach/regs-gpio.h>
 
-#include <asm/plat-s3c24xx/mci.h>
+#include <plat/mci.h>
 
 #include "s3cmci.h"
 
@@ -53,8 +60,6 @@ static const int dbgmap_debug = dbg_err | dbg_debug;
                dev_dbg(&host->pdev->dev, args);  \
        } while (0)
 
-#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
-
 static struct s3c2410_dma_client s3cmci_dma_client = {
        .name           = "s3c-mci",
 };
@@ -159,6 +164,40 @@ static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { }
 
 #endif /* CONFIG_MMC_DEBUG */
 
+/**
+ * s3cmci_host_usedma - return whether the host is using dma or pio
+ * @host: The host state
+ *
+ * Return true if the host is using DMA to transfer data, else false
+ * to use PIO mode. Will return static data depending on the driver
+ * configuration.
+ */
+static inline bool s3cmci_host_usedma(struct s3cmci_host *host)
+{
+#ifdef CONFIG_MMC_S3C_PIO
+       return false;
+#elif defined(CONFIG_MMC_S3C_DMA)
+       return true;
+#else
+       return host->dodma;
+#endif
+}
+
+/**
+ * s3cmci_host_canpio - return true if host has pio code available
+ *
+ * Return true if the driver has been compiled with the PIO support code
+ * available.
+ */
+static inline bool s3cmci_host_canpio(void)
+{
+#ifdef CONFIG_MMC_S3C_PIO
+       return true;
+#else
+       return false;
+#endif
+}
+
 static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
 {
        u32 newmask;
@@ -185,11 +224,37 @@ static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
 
 static inline void clear_imask(struct s3cmci_host *host)
 {
-       writel(0, host->base + host->sdiimsk);
+       u32 mask = readl(host->base + host->sdiimsk);
+
+       /* preserve the SDIO IRQ mask state */
+       mask &= S3C2410_SDIIMSK_SDIOIRQ;
+       writel(mask, host->base + host->sdiimsk);
+}
+
+/**
+ * s3cmci_check_sdio_irq - test whether the SDIO IRQ is being signalled
+ * @host: The host to check.
+ *
+ * Test to see if the SDIO interrupt is being signalled in case the
+ * controller has failed to re-detect a card interrupt. Read GPE8 and
+ * see if it is low and if so, signal a SDIO interrupt.
+ *
+ * This is currently called if a request is finished (we assume that the
+ * bus is now idle) and when the SDIO IRQ is enabled in case the IRQ is
+ * already being indicated.
+*/
+static void s3cmci_check_sdio_irq(struct s3cmci_host *host)
+{
+       if (host->sdio_irqen) {
+               if (gpio_get_value(S3C2410_GPE(8)) == 0) {
+                       printk(KERN_DEBUG "%s: signalling irq\n", __func__);
+                       mmc_signal_sdio_irq(host->mmc);
+               }
+       }
 }
 
 static inline int get_data_buffer(struct s3cmci_host *host,
-                                 u32 *words, u32 **pointer)
+                                 u32 *bytes, u32 **pointer)
 {
        struct scatterlist *sg;
 
@@ -206,7 +271,7 @@ static inline int get_data_buffer(struct s3cmci_host *host,
        }
        sg = &host->mrq->data->sg[host->pio_sgptr];
 
-       *words = sg->length >> 2;
+       *bytes = sg->length;
        *pointer = sg_virt(sg);
 
        host->pio_sgptr++;
@@ -222,7 +287,7 @@ static inline u32 fifo_count(struct s3cmci_host *host)
        u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
 
        fifostat &= S3C2410_SDIFSTA_COUNTMASK;
-       return fifostat >> 2;
+       return fifostat;
 }
 
 static inline u32 fifo_free(struct s3cmci_host *host)
@@ -230,13 +295,73 @@ static inline u32 fifo_free(struct s3cmci_host *host)
        u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
 
        fifostat &= S3C2410_SDIFSTA_COUNTMASK;
-       return (63 - fifostat) >> 2;
+       return 63 - fifostat;
+}
+
+/**
+ * s3cmci_enable_irq - enable IRQ, after having disabled it.
+ * @host: The device state.
+ * @more: True if more IRQs are expected from transfer.
+ *
+ * Enable the main IRQ if needed after it has been disabled.
+ *
+ * The IRQ can be one of the following states:
+ *     - disabled during IDLE
+ *     - disabled whilst processing data
+ *     - enabled during transfer
+ *     - enabled whilst awaiting SDIO interrupt detection
+ */
+static void s3cmci_enable_irq(struct s3cmci_host *host, bool more)
+{
+       unsigned long flags;
+       bool enable = false;
+
+       local_irq_save(flags);
+
+       host->irq_enabled = more;
+       host->irq_disabled = false;
+
+       enable = more | host->sdio_irqen;
+
+       if (host->irq_state != enable) {
+               host->irq_state = enable;
+
+               if (enable)
+                       enable_irq(host->irq);
+               else
+                       disable_irq(host->irq);
+       }
+
+       local_irq_restore(flags);
+}
+
+/**
+ *
+ */
+static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       //printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer);
+
+       host->irq_disabled = transfer;
+
+       if (transfer && host->irq_state) {
+               host->irq_state = false;
+               disable_irq(host->irq);
+       }
+
+       local_irq_restore(flags);
 }
 
 static void do_pio_read(struct s3cmci_host *host)
 {
        int res;
        u32 fifo;
+       u32 *ptr;
+       u32 fifo_words;
        void __iomem *from_ptr;
 
        /* write real prescaler to host, it might be set slow to fix */
@@ -245,8 +370,8 @@ static void do_pio_read(struct s3cmci_host *host)
        from_ptr = host->base + host->sdidata;
 
        while ((fifo = fifo_count(host))) {
-               if (!host->pio_words) {
-                       res = get_data_buffer(host, &host->pio_words,
+               if (!host->pio_bytes) {
+                       res = get_data_buffer(host, &host->pio_bytes,
                                              &host->pio_ptr);
                        if (res) {
                                host->pio_active = XFER_NONE;
@@ -259,26 +384,47 @@ static void do_pio_read(struct s3cmci_host *host)
 
                        dbg(host, dbg_pio,
                            "pio_read(): new target: [%i]@[%p]\n",
-                           host->pio_words, host->pio_ptr);
+                           host->pio_bytes, host->pio_ptr);
                }
 
                dbg(host, dbg_pio,
                    "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
-                   fifo, host->pio_words,
+                   fifo, host->pio_bytes,
                    readl(host->base + S3C2410_SDIDCNT));
 
-               if (fifo > host->pio_words)
-                       fifo = host->pio_words;
+               /* If we have reached the end of the block, we can
+                * read a word and get 1 to 3 bytes.  If we in the
+                * middle of the block, we have to read full words,
+                * otherwise we will write garbage, so round down to
+                * an even multiple of 4. */
+               if (fifo >= host->pio_bytes)
+                       fifo = host->pio_bytes;
+               else
+                       fifo -= fifo & 3;
 
-               host->pio_words -= fifo;
+               host->pio_bytes -= fifo;
                host->pio_count += fifo;
 
-               while (fifo--)
-                       *(host->pio_ptr++) = readl(from_ptr);
+               fifo_words = fifo >> 2;
+               ptr = host->pio_ptr;
+               while (fifo_words--)
+                       *ptr++ = readl(from_ptr);
+               host->pio_ptr = ptr;
+
+               if (fifo & 3) {
+                       u32 n = fifo & 3;
+                       u32 data = readl(from_ptr);
+                       u8 *p = (u8 *)host->pio_ptr;
+
+                       while (n--) {
+                               *p++ = data;
+                               data >>= 8;
+                       }
+               }
        }
 
-       if (!host->pio_words) {
-               res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
+       if (!host->pio_bytes) {
+               res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr);
                if (res) {
                        dbg(host, dbg_pio,
                            "pio_read(): complete (no more buffers).\n");
@@ -298,12 +444,13 @@ static void do_pio_write(struct s3cmci_host *host)
        void __iomem *to_ptr;
        int res;
        u32 fifo;
+       u32 *ptr;
 
        to_ptr = host->base + host->sdidata;
 
-       while ((fifo = fifo_free(host))) {
-               if (!host->pio_words) {
-                       res = get_data_buffer(host, &host->pio_words,
+       while ((fifo = fifo_free(host)) > 3) {
+               if (!host->pio_bytes) {
+                       res = get_data_buffer(host, &host->pio_bytes,
                                                        &host->pio_ptr);
                        if (res) {
                                dbg(host, dbg_pio,
@@ -315,18 +462,27 @@ static void do_pio_write(struct s3cmci_host *host)
 
                        dbg(host, dbg_pio,
                            "pio_write(): new source: [%i]@[%p]\n",
-                           host->pio_words, host->pio_ptr);
+                           host->pio_bytes, host->pio_ptr);
 
                }
 
-               if (fifo > host->pio_words)
-                       fifo = host->pio_words;
+               /* If we have reached the end of the block, we have to
+                * write exactly the remaining number of bytes.  If we
+                * in the middle of the block, we have to write full
+                * words, so round down to an even multiple of 4. */
+               if (fifo >= host->pio_bytes)
+                       fifo = host->pio_bytes;
+               else
+                       fifo -= fifo & 3;
 
-               host->pio_words -= fifo;
+               host->pio_bytes -= fifo;
                host->pio_count += fifo;
 
+               fifo = (fifo + 3) >> 2;
+               ptr = host->pio_ptr;
                while (fifo--)
-                       writel(*(host->pio_ptr++), to_ptr);
+                       writel(*ptr++, to_ptr);
+               host->pio_ptr = ptr;
        }
 
        enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
@@ -336,8 +492,7 @@ static void pio_tasklet(unsigned long data)
 {
        struct s3cmci_host *host = (struct s3cmci_host *) data;
 
-
-       disable_irq(host->irq);
+       s3cmci_disable_irq(host, true);
 
        if (host->pio_active == XFER_WRITE)
                do_pio_write(host);
@@ -349,17 +504,18 @@ static void pio_tasklet(unsigned long data)
                clear_imask(host);
                if (host->pio_active != XFER_NONE) {
                        dbg(host, dbg_err, "unfinished %s "
-                           "- pio_count:[%u] pio_words:[%u]\n",
+                           "- pio_count:[%u] pio_bytes:[%u]\n",
                            (host->pio_active == XFER_READ) ? "read" : "write",
-                           host->pio_count, host->pio_words);
+                           host->pio_count, host->pio_bytes);
 
                        if (host->mrq->data)
                                host->mrq->data->error = -EINVAL;
                }
 
+               s3cmci_enable_irq(host, false);
                finalize_request(host);
        } else
-               enable_irq(host->irq);
+               s3cmci_enable_irq(host, true);
 }
 
 /*
@@ -394,17 +550,27 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
        struct s3cmci_host *host = dev_id;
        struct mmc_command *cmd;
        u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
-       u32 mci_cclear, mci_dclear;
+       u32 mci_cclear = 0, mci_dclear;
        unsigned long iflags;
 
+       mci_dsta = readl(host->base + S3C2410_SDIDSTA);
+       mci_imsk = readl(host->base + host->sdiimsk);
+
+       if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {
+               if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
+                       mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
+                       writel(mci_dclear, host->base + S3C2410_SDIDSTA);
+
+                       mmc_signal_sdio_irq(host->mmc);
+                       return IRQ_HANDLED;
+               }
+       }
+
        spin_lock_irqsave(&host->complete_lock, iflags);
 
        mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
-       mci_dsta = readl(host->base + S3C2410_SDIDSTA);
        mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
        mci_fsta = readl(host->base + S3C2410_SDIFSTA);
-       mci_imsk = readl(host->base + host->sdiimsk);
-       mci_cclear = 0;
        mci_dclear = 0;
 
        if ((host->complete_what == COMPLETION_NONE) ||
@@ -428,7 +594,7 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
                goto irq_out;
        }
 
-       if (!host->dodma) {
+       if (!s3cmci_host_usedma(host)) {
                if ((host->pio_active == XFER_WRITE) &&
                    (mci_fsta & S3C2410_SDIFSTA_TFDET)) {
 
@@ -635,6 +801,7 @@ static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
        dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
                size, mci_dsta, mci_dcnt);
 
+       host->dma_complete = 1;
        host->complete_what = COMPLETION_FINALIZE;
 
 out:
@@ -645,15 +812,15 @@ out:
 fail_request:
        host->mrq->data->error = -EINVAL;
        host->complete_what = COMPLETION_FINALIZE;
-       writel(0, host->base + host->sdiimsk);
-       goto out;
+       clear_imask(host);
 
+       goto out;
 }
 
 static void finalize_request(struct s3cmci_host *host)
 {
        struct mmc_request *mrq = host->mrq;
-       struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+       struct mmc_command *cmd;
        int debug_as_failure = 0;
 
        if (host->complete_what != COMPLETION_FINALIZE)
@@ -661,11 +828,13 @@ static void finalize_request(struct s3cmci_host *host)
 
        if (!mrq)
                return;
+       cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
 
        if (cmd->data && (cmd->error == 0) &&
            (cmd->data->error == 0)) {
-               if (host->dodma && (!host->dma_complete)) {
-                       dbg(host, dbg_dma, "DMA Missing!\n");
+               if (s3cmci_host_usedma(host) && (!host->dma_complete)) {
+                       dbg(host, dbg_dma, "DMA Missing (%d)!\n",
+                           host->dma_complete);
                        return;
                }
        }
@@ -690,7 +859,7 @@ static void finalize_request(struct s3cmci_host *host)
        writel(0, host->base + S3C2410_SDICMDARG);
        writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
        writel(0, host->base + S3C2410_SDICMDCON);
-       writel(0, host->base + host->sdiimsk);
+       clear_imask(host);
 
        if (cmd->data && cmd->error)
                cmd->data->error = cmd->error;
@@ -716,7 +885,7 @@ static void finalize_request(struct s3cmci_host *host)
        /* If we had an error while transfering data we flush the
         * DMA channel and the fifo to clear out any garbage. */
        if (mrq->data->error != 0) {
-               if (host->dodma)
+               if (s3cmci_host_usedma(host))
                        s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
 
                if (host->is2440) {
@@ -738,6 +907,8 @@ static void finalize_request(struct s3cmci_host *host)
 request_done:
        host->complete_what = COMPLETION_NONE;
        host->mrq = NULL;
+
+       s3cmci_check_sdio_irq(host);
        mmc_request_done(host->mmc, mrq);
 }
 
@@ -752,12 +923,11 @@ static void s3cmci_dma_setup(struct s3cmci_host *host,
 
        last_source = source;
 
-       s3c2410_dma_devconfig(host->dma, source, 3,
+       s3c2410_dma_devconfig(host->dma, source,
                              host->mem->start + host->sdidata);
 
        if (!setup_ok) {
-               s3c2410_dma_config(host->dma, 4,
-                       (S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));
+               s3c2410_dma_config(host->dma, 4);
                s3c2410_dma_set_buffdone_fn(host->dma,
                                            s3cmci_dma_done_callback);
                s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
@@ -812,11 +982,10 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
                /* We cannot deal with unaligned blocks with more than
                 * one block being transfered. */
 
-               if (data->blocks > 1)
+               if (data->blocks > 1) {
+                       pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
                        return -EINVAL;
-
-               /* No support yet for non-word block transfers. */
-               return -EINVAL;
+               }
        }
 
        while (readl(host->base + S3C2410_SDIDSTA) &
@@ -836,7 +1005,7 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
 
        dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
 
-       if (host->dodma)
+       if (s3cmci_host_usedma(host))
                dcon |= S3C2410_SDIDCON_DMAEN;
 
        if (host->bus_width == MMC_BUS_WIDTH_4)
@@ -896,7 +1065,7 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
        BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
 
        host->pio_sgptr = 0;
-       host->pio_words = 0;
+       host->pio_bytes = 0;
        host->pio_count = 0;
        host->pio_active = rw ? XFER_WRITE : XFER_READ;
 
@@ -914,7 +1083,7 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
 static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
 {
        int dma_len, i;
-       int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+       int rw = data->flags & MMC_DATA_WRITE;
 
        BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
 
@@ -922,7 +1091,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
        s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
 
        dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
-                            (rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+                            rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 
        if (dma_len == 0)
                return -ENOMEM;
@@ -933,11 +1102,11 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
        for (i = 0; i < dma_len; i++) {
                int res;
 
-               dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
-                       sg_dma_address(&data->sg[i]),
-                       sg_dma_len(&data->sg[i]));
+               dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i,
+                   sg_dma_address(&data->sg[i]),
+                   sg_dma_len(&data->sg[i]));
 
-               res = s3c2410_dma_enqueue(host->dma, (void *) host,
+               res = s3c2410_dma_enqueue(host->dma, host,
                                          sg_dma_address(&data->sg[i]),
                                          sg_dma_len(&data->sg[i]));
 
@@ -982,7 +1151,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
                        return;
                }
 
-               if (host->dodma)
+               if (s3cmci_host_usedma(host))
                        res = s3cmci_prepare_dma(host, cmd->data);
                else
                        res = s3cmci_prepare_pio(host, cmd->data);
@@ -1001,7 +1170,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
        s3cmci_send_command(host, cmd);
 
        /* Enable Interrupt */
-       enable_irq(host->irq);
+       s3cmci_enable_irq(host, true);
 }
 
 static int s3cmci_card_present(struct mmc_host *mmc)
@@ -1010,10 +1179,10 @@ static int s3cmci_card_present(struct mmc_host *mmc)
        struct s3c24xx_mci_pdata *pdata = host->pdata;
        int ret;
 
-       if (pdata->gpio_detect == 0)
+       if (pdata->no_detect)
                return -ENOSYS;
 
-       ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1;
+       ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1;
        return ret ^ pdata->detect_invert;
 }
 
@@ -1033,10 +1202,33 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                s3cmci_send_request(mmc);
 }
 
+static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)
+{
+       u32 mci_psc;
+
+       /* Set clock */
+       for (mci_psc = 0; mci_psc < 255; mci_psc++) {
+               host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
+
+               if (host->real_rate <= ios->clock)
+                       break;
+       }
+
+       if (mci_psc > 255)
+               mci_psc = 255;
+
+       host->prescaler = mci_psc;
+       writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+       /* If requested clock is 0, real_rate will be 0, too */
+       if (ios->clock == 0)
+               host->real_rate = 0;
+}
+
 static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct s3cmci_host *host = mmc_priv(mmc);
-       u32 mci_psc, mci_con;
+       u32 mci_con;
 
        /* Set the power state */
 
@@ -1045,12 +1237,12 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        switch (ios->power_mode) {
        case MMC_POWER_ON:
        case MMC_POWER_UP:
-               s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
-               s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
-               s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
-               s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
-               s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
-               s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+               s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK);
+               s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD);
+               s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0);
+               s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1);
+               s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2);
+               s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3);
 
                if (host->pdata->set_power)
                        host->pdata->set_power(ios->power_mode, ios->vdd);
@@ -1062,8 +1254,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        case MMC_POWER_OFF:
        default:
-               s3c2410_gpio_setpin(S3C2410_GPE5, 0);
-               s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+               gpio_direction_output(S3C2410_GPE(5), 0);
 
                if (host->is2440)
                        mci_con |= S3C2440_SDICON_SDRESET;
@@ -1074,23 +1265,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                break;
        }
 
-       /* Set clock */
-       for (mci_psc = 0; mci_psc < 255; mci_psc++) {
-               host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
-
-               if (host->real_rate <= ios->clock)
-                       break;
-       }
-
-       if (mci_psc > 255)
-               mci_psc = 255;
-
-       host->prescaler = mci_psc;
-       writel(host->prescaler, host->base + S3C2410_SDIPRE);
-
-       /* If requested clock is 0, real_rate will be 0, too */
-       if (ios->clock == 0)
-               host->real_rate = 0;
+       s3cmci_set_clk(host, ios);
 
        /* Set CLOCK_ENABLE */
        if (ios->clock)
@@ -1125,34 +1300,259 @@ static int s3cmci_get_ro(struct mmc_host *mmc)
        struct s3c24xx_mci_pdata *pdata = host->pdata;
        int ret;
 
-       if (pdata->gpio_wprotect == 0)
+       if (pdata->no_wprotect)
                return 0;
 
-       ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
-
-       if (pdata->wprotect_invert)
-               ret = !ret;
+       ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;
+       ret ^= pdata->wprotect_invert;
 
        return ret;
 }
 
+static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct s3cmci_host *host = mmc_priv(mmc);
+       unsigned long flags;
+       u32 con;
+
+       local_irq_save(flags);
+
+       con = readl(host->base + S3C2410_SDICON);
+       host->sdio_irqen = enable;
+
+       if (enable == host->sdio_irqen)
+               goto same_state;
+
+       if (enable) {
+               con |= S3C2410_SDICON_SDIOIRQ;
+               enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);
+
+               if (!host->irq_state && !host->irq_disabled) {
+                       host->irq_state = true;
+                       enable_irq(host->irq);
+               }
+       } else {
+               disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);
+               con &= ~S3C2410_SDICON_SDIOIRQ;
+
+               if (!host->irq_enabled && host->irq_state) {
+                       disable_irq_nosync(host->irq);
+                       host->irq_state = false;
+               }
+       }
+
+       writel(con, host->base + S3C2410_SDICON);
+
+ same_state:
+       local_irq_restore(flags);
+
+       s3cmci_check_sdio_irq(host);
+}
+
 static struct mmc_host_ops s3cmci_ops = {
        .request        = s3cmci_request,
        .set_ios        = s3cmci_set_ios,
        .get_ro         = s3cmci_get_ro,
        .get_cd         = s3cmci_card_present,
+       .enable_sdio_irq = s3cmci_enable_sdio_irq,
 };
 
 static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
        /* This is currently here to avoid a number of if (host->pdata)
-        * checks. Any zero fields to ensure reaonable defaults are picked. */
+        * checks. Any zero fields to ensure reasonable defaults are picked. */
+        .no_wprotect = 1,
+        .no_detect = 1,
 };
 
-static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
+#ifdef CONFIG_CPU_FREQ
+
+static int s3cmci_cpufreq_transition(struct notifier_block *nb,
+                                    unsigned long val, void *data)
+{
+       struct s3cmci_host *host;
+       struct mmc_host *mmc;
+       unsigned long newclk;
+       unsigned long flags;
+
+       host = container_of(nb, struct s3cmci_host, freq_transition);
+       newclk = clk_get_rate(host->clk);
+       mmc = host->mmc;
+
+       if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) ||
+           (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) {
+               spin_lock_irqsave(&mmc->lock, flags);
+
+               host->clk_rate = newclk;
+
+               if (mmc->ios.power_mode != MMC_POWER_OFF &&
+                   mmc->ios.clock != 0)
+                       s3cmci_set_clk(host, &mmc->ios);
+
+               spin_unlock_irqrestore(&mmc->lock, flags);
+       }
+
+       return 0;
+}
+
+static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
+{
+       host->freq_transition.notifier_call = s3cmci_cpufreq_transition;
+
+       return cpufreq_register_notifier(&host->freq_transition,
+                                        CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
+{
+       cpufreq_unregister_notifier(&host->freq_transition,
+                                   CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
+{
+       return 0;
+}
+
+static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
+{
+}
+#endif
+
+
+#ifdef CONFIG_DEBUG_FS
+
+static int s3cmci_state_show(struct seq_file *seq, void *v)
+{
+       struct s3cmci_host *host = seq->private;
+
+       seq_printf(seq, "Register base = 0x%08x\n", (u32)host->base);
+       seq_printf(seq, "Clock rate = %ld\n", host->clk_rate);
+       seq_printf(seq, "Prescale = %d\n", host->prescaler);
+       seq_printf(seq, "is2440 = %d\n", host->is2440);
+       seq_printf(seq, "IRQ = %d\n", host->irq);
+       seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled);
+       seq_printf(seq, "IRQ disabled = %d\n", host->irq_disabled);
+       seq_printf(seq, "IRQ state = %d\n", host->irq_state);
+       seq_printf(seq, "CD IRQ = %d\n", host->irq_cd);
+       seq_printf(seq, "Do DMA = %d\n", s3cmci_host_usedma(host));
+       seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk);
+       seq_printf(seq, "SDIDATA at %d\n", host->sdidata);
+
+       return 0;
+}
+
+static int s3cmci_state_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, s3cmci_state_show, inode->i_private);
+}
+
+static const struct file_operations s3cmci_fops_state = {
+       .owner          = THIS_MODULE,
+       .open           = s3cmci_state_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+#define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r }
+
+struct s3cmci_reg {
+       unsigned short  addr;
+       unsigned char   *name;
+} debug_regs[] = {
+       DBG_REG(CON),
+       DBG_REG(PRE),
+       DBG_REG(CMDARG),
+       DBG_REG(CMDCON),
+       DBG_REG(CMDSTAT),
+       DBG_REG(RSP0),
+       DBG_REG(RSP1),
+       DBG_REG(RSP2),
+       DBG_REG(RSP3),
+       DBG_REG(TIMER),
+       DBG_REG(BSIZE),
+       DBG_REG(DCON),
+       DBG_REG(DCNT),
+       DBG_REG(DSTA),
+       DBG_REG(FSTA),
+       {}
+};
+
+static int s3cmci_regs_show(struct seq_file *seq, void *v)
+{
+       struct s3cmci_host *host = seq->private;
+       struct s3cmci_reg *rptr = debug_regs;
+
+       for (; rptr->name; rptr++)
+               seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name,
+                          readl(host->base + rptr->addr));
+
+       seq_printf(seq, "SDIIMSK\t=0x%08x\n", readl(host->base + host->sdiimsk));
+
+       return 0;
+}
+
+static int s3cmci_regs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, s3cmci_regs_show, inode->i_private);
+}
+
+static const struct file_operations s3cmci_fops_regs = {
+       .owner          = THIS_MODULE,
+       .open           = s3cmci_regs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void s3cmci_debugfs_attach(struct s3cmci_host *host)
+{
+       struct device *dev = &host->pdev->dev;
+
+       host->debug_root = debugfs_create_dir(dev_name(dev), NULL);
+       if (IS_ERR(host->debug_root)) {
+               dev_err(dev, "failed to create debugfs root\n");
+               return;
+       }
+
+       host->debug_state = debugfs_create_file("state", 0444,
+                                               host->debug_root, host,
+                                               &s3cmci_fops_state);
+
+       if (IS_ERR(host->debug_state))
+               dev_err(dev, "failed to create debug state file\n");
+
+       host->debug_regs = debugfs_create_file("regs", 0444,
+                                              host->debug_root, host,
+                                              &s3cmci_fops_regs);
+
+       if (IS_ERR(host->debug_regs))
+               dev_err(dev, "failed to create debug regs file\n");
+}
+
+static void s3cmci_debugfs_remove(struct s3cmci_host *host)
+{
+       debugfs_remove(host->debug_regs);
+       debugfs_remove(host->debug_state);
+       debugfs_remove(host->debug_root);
+}
+
+#else
+static inline void s3cmci_debugfs_attach(struct s3cmci_host *host) { }
+static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { }
+
+#endif /* CONFIG_DEBUG_FS */
+
+static int __devinit s3cmci_probe(struct platform_device *pdev)
 {
        struct s3cmci_host *host;
        struct mmc_host *mmc;
        int ret;
+       int is2440;
+       int i;
+
+       is2440 = platform_get_device_id(pdev)->driver_data;
 
        mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
        if (!mmc) {
@@ -1160,6 +1560,18 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
                goto probe_out;
        }
 
+       for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {
+               ret = gpio_request(i, dev_name(&pdev->dev));
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get gpio %d\n", i);
+
+                       for (i--; i >= S3C2410_GPE(5); i--)
+                               gpio_free(i);
+
+                       goto probe_free_host;
+               }
+       }
+
        host = mmc_priv(mmc);
        host->mmc       = mmc;
        host->pdev      = pdev;
@@ -1184,11 +1596,12 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
                host->clk_div   = 2;
        }
 
-       host->dodma             = 0;
        host->complete_what     = COMPLETION_NONE;
        host->pio_active        = XFER_NONE;
 
-       host->dma               = S3CMCI_DMA;
+#ifdef CONFIG_MMC_S3C_PIODMA
+       host->dodma             = host->pdata->dma;
+#endif
 
        host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!host->mem) {
@@ -1196,19 +1609,19 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
                        "failed to get io memory region resouce.\n");
 
                ret = -ENOENT;
-               goto probe_free_host;
+               goto probe_free_gpio;
        }
 
        host->mem = request_mem_region(host->mem->start,
-                                      RESSIZE(host->mem), pdev->name);
+                                      resource_size(host->mem), pdev->name);
 
        if (!host->mem) {
                dev_err(&pdev->dev, "failed to request io memory region.\n");
                ret = -ENOENT;
-               goto probe_free_host;
+               goto probe_free_gpio;
        }
 
-       host->base = ioremap(host->mem->start, RESSIZE(host->mem));
+       host->base = ioremap(host->mem->start, resource_size(host->mem));
        if (!host->base) {
                dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
                ret = -EINVAL;
@@ -1233,31 +1646,60 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
         * ensure we don't lock the system with un-serviceable requests. */
 
        disable_irq(host->irq);
+       host->irq_state = false;
 
-       host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
-
-       if (host->irq_cd >= 0) {
-               if (request_irq(host->irq_cd, s3cmci_irq_cd,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               DRIVER_NAME, host)) {
-                       dev_err(&pdev->dev, "can't get card detect irq.\n");
-                       ret = -ENOENT;
+       if (!host->pdata->no_detect) {
+               ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get detect gpio\n");
                        goto probe_free_irq;
                }
-       } else {
-               dev_warn(&pdev->dev, "host detect has no irq available\n");
-               s3c2410_gpio_cfgpin(host->pdata->gpio_detect,
-                                   S3C2410_GPIO_INPUT);
+
+               host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
+
+               if (host->irq_cd >= 0) {
+                       if (request_irq(host->irq_cd, s3cmci_irq_cd,
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING,
+                                       DRIVER_NAME, host)) {
+                               dev_err(&pdev->dev,
+                                       "can't get card detect irq.\n");
+                               ret = -ENOENT;
+                               goto probe_free_gpio_cd;
+                       }
+               } else {
+                       dev_warn(&pdev->dev,
+                                "host detect has no irq available\n");
+                       gpio_direction_input(host->pdata->gpio_detect);
+               }
+       } else
+               host->irq_cd = -1;
+
+       if (!host->pdata->no_wprotect) {
+               ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get writeprotect\n");
+                       goto probe_free_irq_cd;
+               }
+
+               gpio_direction_input(host->pdata->gpio_wprotect);
        }
 
-       if (host->pdata->gpio_wprotect)
-               s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
-                                   S3C2410_GPIO_INPUT);
+       /* depending on the dma state, get a dma channel to use. */
 
-       if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) {
-               dev_err(&pdev->dev, "unable to get DMA channel.\n");
-               ret = -EBUSY;
-               goto probe_free_irq_cd;
+       if (s3cmci_host_usedma(host)) {
+               host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
+                                               host);
+               if (host->dma < 0) {
+                       dev_err(&pdev->dev, "cannot get DMA channel.\n");
+                       if (!s3cmci_host_canpio()) {
+                               ret = -EBUSY;
+                               goto probe_free_gpio_wp;
+                       } else {
+                               dev_warn(&pdev->dev, "falling back to PIO.\n");
+                               host->dodma = 0;
+                       }
+               }
        }
 
        host->clk = clk_get(&pdev->dev, "sdi");
@@ -1265,7 +1707,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
                dev_err(&pdev->dev, "failed to find clock source.\n");
                ret = PTR_ERR(host->clk);
                host->clk = NULL;
-               goto probe_free_host;
+               goto probe_free_dma;
        }
 
        ret = clk_enable(host->clk);
@@ -1278,7 +1720,11 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
 
        mmc->ops        = &s3cmci_ops;
        mmc->ocr_avail  = MMC_VDD_32_33 | MMC_VDD_33_34;
+#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
+       mmc->caps       = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+#else
        mmc->caps       = MMC_CAP_4_BIT_DATA;
+#endif
        mmc->f_min      = host->clk_rate / (host->clk_div * 256);
        mmc->f_max      = host->clk_rate / host->clk_div;
 
@@ -1298,23 +1744,48 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
            (host->is2440?"2440":""),
            host->base, host->irq, host->irq_cd, host->dma);
 
+       ret = s3cmci_cpufreq_register(host);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register cpufreq\n");
+               goto free_dmabuf;
+       }
+
        ret = mmc_add_host(mmc);
        if (ret) {
                dev_err(&pdev->dev, "failed to add mmc host.\n");
-               goto free_dmabuf;
+               goto free_cpufreq;
        }
 
+       s3cmci_debugfs_attach(host);
+
        platform_set_drvdata(pdev, mmc);
-       dev_info(&pdev->dev, "initialisation done.\n");
+       dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),
+                s3cmci_host_usedma(host) ? "dma" : "pio",
+                mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");
 
        return 0;
 
+ free_cpufreq:
+       s3cmci_cpufreq_deregister(host);
+
  free_dmabuf:
        clk_disable(host->clk);
 
  clk_free:
        clk_put(host->clk);
 
+ probe_free_dma:
+       if (s3cmci_host_usedma(host))
+               s3c2410_dma_free(host->dma, &s3cmci_dma_client);
+
+ probe_free_gpio_wp:
+       if (!host->pdata->no_wprotect)
+               gpio_free(host->pdata->gpio_wprotect);
+
+ probe_free_gpio_cd:
+       if (!host->pdata->no_detect)
+               gpio_free(host->pdata->gpio_detect);
+
  probe_free_irq_cd:
        if (host->irq_cd >= 0)
                free_irq(host->irq_cd, host);
@@ -1326,10 +1797,15 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
        iounmap(host->base);
 
  probe_free_mem_region:
-       release_mem_region(host->mem->start, RESSIZE(host->mem));
+       release_mem_region(host->mem->start, resource_size(host->mem));
+
+ probe_free_gpio:
+       for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
+               gpio_free(i);
 
  probe_free_host:
        mmc_free_host(mmc);
+
  probe_out:
        return ret;
 }
@@ -1342,6 +1818,8 @@ static void s3cmci_shutdown(struct platform_device *pdev)
        if (host->irq_cd >= 0)
                free_irq(host->irq_cd, host);
 
+       s3cmci_debugfs_remove(host);
+       s3cmci_cpufreq_deregister(host);
        mmc_remove_host(mmc);
        clk_disable(host->clk);
 }
@@ -1350,104 +1828,101 @@ static int __devexit s3cmci_remove(struct platform_device *pdev)
 {
        struct mmc_host         *mmc  = platform_get_drvdata(pdev);
        struct s3cmci_host      *host = mmc_priv(mmc);
+       struct s3c24xx_mci_pdata *pd = host->pdata;
+       int i;
 
        s3cmci_shutdown(pdev);
 
        clk_put(host->clk);
 
        tasklet_disable(&host->pio_tasklet);
-       s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
+
+       if (s3cmci_host_usedma(host))
+               s3c2410_dma_free(host->dma, &s3cmci_dma_client);
 
        free_irq(host->irq, host);
 
+       if (!pd->no_wprotect)
+               gpio_free(pd->gpio_wprotect);
+
+       if (!pd->no_detect)
+               gpio_free(pd->gpio_detect);
+
+       for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
+               gpio_free(i);
+
+
        iounmap(host->base);
-       release_mem_region(host->mem->start, RESSIZE(host->mem));
+       release_mem_region(host->mem->start, resource_size(host->mem));
 
        mmc_free_host(mmc);
        return 0;
 }
 
-static int __devinit s3cmci_2410_probe(struct platform_device *dev)
-{
-       return s3cmci_probe(dev, 0);
-}
+static struct platform_device_id s3cmci_driver_ids[] = {
+       {
+               .name   = "s3c2410-sdi",
+               .driver_data    = 0,
+       }, {
+               .name   = "s3c2412-sdi",
+               .driver_data    = 1,
+       }, {
+               .name   = "s3c2440-sdi",
+               .driver_data    = 1,
+       },
+       { }
+};
 
-static int __devinit s3cmci_2412_probe(struct platform_device *dev)
-{
-       return s3cmci_probe(dev, 1);
-}
+MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
 
-static int __devinit s3cmci_2440_probe(struct platform_device *dev)
-{
-       return s3cmci_probe(dev, 1);
-}
 
 #ifdef CONFIG_PM
 
-static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
+static int s3cmci_suspend(struct device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
 
-       return  mmc_suspend_host(mmc, state);
+       return mmc_suspend_host(mmc);
 }
 
-static int s3cmci_resume(struct platform_device *dev)
+static int s3cmci_resume(struct device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
 
        return mmc_resume_host(mmc);
 }
 
-#else /* CONFIG_PM */
-#define s3cmci_suspend NULL
-#define s3cmci_resume NULL
-#endif /* CONFIG_PM */
-
-
-static struct platform_driver s3cmci_2410_driver = {
-       .driver.name    = "s3c2410-sdi",
-       .driver.owner   = THIS_MODULE,
-       .probe          = s3cmci_2410_probe,
-       .remove         = __devexit_p(s3cmci_remove),
-       .shutdown       = s3cmci_shutdown,
+static const struct dev_pm_ops s3cmci_pm = {
        .suspend        = s3cmci_suspend,
        .resume         = s3cmci_resume,
 };
 
-static struct platform_driver s3cmci_2412_driver = {
-       .driver.name    = "s3c2412-sdi",
-       .driver.owner   = THIS_MODULE,
-       .probe          = s3cmci_2412_probe,
-       .remove         = __devexit_p(s3cmci_remove),
-       .shutdown       = s3cmci_shutdown,
-       .suspend        = s3cmci_suspend,
-       .resume         = s3cmci_resume,
-};
+#define s3cmci_pm_ops &s3cmci_pm
+#else /* CONFIG_PM */
+#define s3cmci_pm_ops NULL
+#endif /* CONFIG_PM */
+
 
-static struct platform_driver s3cmci_2440_driver = {
-       .driver.name    = "s3c2440-sdi",
-       .driver.owner   = THIS_MODULE,
-       .probe          = s3cmci_2440_probe,
+static struct platform_driver s3cmci_driver = {
+       .driver = {
+               .name   = "s3c-sdi",
+               .owner  = THIS_MODULE,
+               .pm     = s3cmci_pm_ops,
+       },
+       .id_table       = s3cmci_driver_ids,
+       .probe          = s3cmci_probe,
        .remove         = __devexit_p(s3cmci_remove),
        .shutdown       = s3cmci_shutdown,
-       .suspend        = s3cmci_suspend,
-       .resume         = s3cmci_resume,
 };
 
-
 static int __init s3cmci_init(void)
 {
-       platform_driver_register(&s3cmci_2410_driver);
-       platform_driver_register(&s3cmci_2412_driver);
-       platform_driver_register(&s3cmci_2440_driver);
-       return 0;
+       return platform_driver_register(&s3cmci_driver);
 }
 
 static void __exit s3cmci_exit(void)
 {
-       platform_driver_unregister(&s3cmci_2410_driver);
-       platform_driver_unregister(&s3cmci_2412_driver);
-       platform_driver_unregister(&s3cmci_2440_driver);
+       platform_driver_unregister(&s3cmci_driver);
 }
 
 module_init(s3cmci_init);
@@ -1455,7 +1930,4 @@ module_exit(s3cmci_exit);
 
 MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
 MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
-MODULE_ALIAS("platform:s3c2410-sdi");
-MODULE_ALIAS("platform:s3c2412-sdi");
-MODULE_ALIAS("platform:s3c2440-sdi");
+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");