]> err.no Git - linux-2.6/blobdiff - drivers/mmc/host/au1xmmc.c
Merge commit 'jk/jk-merge'
[linux-2.6] / drivers / mmc / host / au1xmmc.c
index d8776d61b3cc5c83879f7c3d66fdba4c7fd1fb92..99b20917cc0f9a677cfd414d27d70e01e183133d 100644 (file)
@@ -21,7 +21,7 @@
  * published by the Free Software Foundation.
  */
 
-/* Why is a timer used to detect insert events?
+/* Why don't we use the SD controllers' carddetect feature?
  *
  * From the AU1100 MMC application guide:
  * If the Au1100-based design is intended to support both MultiMediaCards
@@ -30,8 +30,6 @@
  * In doing so, a MMC card never enters SPI-mode communications,
  * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective
  * (the low to high transition will not occur).
- *
- * So we use the timer to check the status manually.
  */
 
 #include <linux/module.h>
 #include <asm/mach-au1x00/au1xxx_dbdma.h>
 #include <asm/mach-au1x00/au1100_mmc.h>
 
-#include <au1xxx.h>
-#include "au1xmmc.h"
-
 #define DRIVER_NAME "au1xxx-mmc"
 
 /* Set this to enable special debugging macros */
 /* #define DEBUG */
 
 #ifdef DEBUG
-#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args)
+#define DBG(fmt, idx, args...) \
+       printk(KERN_DEBUG "au1xmmc(%d): DEBUG: " fmt, idx, ##args)
 #else
-#define DBG(fmt, idx, args...)
+#define DBG(fmt, idx, args...) do {} while (0)
 #endif
 
+/* Hardware definitions */
+#define AU1XMMC_DESCRIPTOR_COUNT 1
+#define AU1XMMC_DESCRIPTOR_SIZE  2048
+
+#define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
+                    MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
+                    MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)
+
+/* This gives us a hard value for the stop command that we can write directly
+ * to the command register.
+ */
+#define STOP_CMD       \
+       (SD_CMD_RT_1B | SD_CMD_CT_7 | (0xC << SD_CMD_CI_SHIFT) | SD_CMD_GO)
+
+/* This is the set of interrupts that we configure by default. */
+#define AU1XMMC_INTERRUPTS                             \
+       (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_RAT |  \
+        SD_CONFIG_CR | SD_CONFIG_I)
+
+/* The poll event (looking for insert/remove events runs twice a second. */
+#define AU1XMMC_DETECT_TIMEOUT (HZ/2)
+
+struct au1xmmc_host {
+       struct mmc_host *mmc;
+       struct mmc_request *mrq;
+
+       u32 flags;
+       u32 iobase;
+       u32 clock;
+       u32 bus_width;
+       u32 power_mode;
+
+       int status;
+
+       struct {
+               int len;
+               int dir;
+       } dma;
+
+       struct {
+               int index;
+               int offset;
+               int len;
+       } pio;
+
+       u32 tx_chan;
+       u32 rx_chan;
+
+       int irq;
+
+       struct tasklet_struct finish_task;
+       struct tasklet_struct data_task;
+       struct au1xmmc_platform_data *platdata;
+       struct platform_device *pdev;
+       struct resource *ioarea;
+};
+
+/* Status flags used by the host structure */
+#define HOST_F_XMIT    0x0001
+#define HOST_F_RECV    0x0002
+#define HOST_F_DMA     0x0010
+#define HOST_F_ACTIVE  0x0100
+#define HOST_F_STOP    0x1000
+
+#define HOST_S_IDLE    0x0001
+#define HOST_S_CMD     0x0002
+#define HOST_S_DATA    0x0003
+#define HOST_S_STOP    0x0004
+
+/* Easy access macros */
+#define HOST_STATUS(h) ((h)->iobase + SD_STATUS)
+#define HOST_CONFIG(h) ((h)->iobase + SD_CONFIG)
+#define HOST_ENABLE(h) ((h)->iobase + SD_ENABLE)
+#define HOST_TXPORT(h) ((h)->iobase + SD_TXPORT)
+#define HOST_RXPORT(h) ((h)->iobase + SD_RXPORT)
+#define HOST_CMDARG(h) ((h)->iobase + SD_CMDARG)
+#define HOST_BLKSIZE(h)        ((h)->iobase + SD_BLKSIZE)
+#define HOST_CMD(h)    ((h)->iobase + SD_CMD)
+#define HOST_CONFIG2(h)        ((h)->iobase + SD_CONFIG2)
+#define HOST_TIMEOUT(h)        ((h)->iobase + SD_TIMEOUT)
+#define HOST_DEBUG(h)  ((h)->iobase + SD_DEBUG)
+
+#define DMA_CHANNEL(h) \
+       (((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
+
 static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)
 {
        u32 val = au_readl(HOST_CONFIG(host));
@@ -95,14 +176,13 @@ static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask)
 
 static inline void SEND_STOP(struct au1xmmc_host *host)
 {
-
-       /* We know the value of CONFIG2, so avoid a read we don't need */
-       u32 mask = SD_CONFIG2_EN;
+       u32 config2;
 
        WARN_ON(host->status != HOST_S_DATA);
        host->status = HOST_S_STOP;
 
-       au_writel(mask | SD_CONFIG2_DF, HOST_CONFIG2(host));
+       config2 = au_readl(HOST_CONFIG2(host));
+       au_writel(config2 | SD_CONFIG2_DF, HOST_CONFIG2(host));
        au_sync();
 
        /* Send the stop commmand */
@@ -115,34 +195,28 @@ static void au1xmmc_set_power(struct au1xmmc_host *host, int state)
                host->platdata->set_power(host->mmc, state);
 }
 
-static int au1xmmc_card_inserted(struct au1xmmc_host *host)
+static int au1xmmc_card_inserted(struct mmc_host *mmc)
 {
-       int ret;
+       struct au1xmmc_host *host = mmc_priv(mmc);
 
        if (host->platdata && host->platdata->card_inserted)
-               ret = host->platdata->card_inserted(host->mmc);
-       else
-               ret = 1;        /* assume there is a card */
+               return !!host->platdata->card_inserted(host->mmc);
 
-       return ret;
+       return -ENOSYS;
 }
 
 static int au1xmmc_card_readonly(struct mmc_host *mmc)
 {
        struct au1xmmc_host *host = mmc_priv(mmc);
-       int ret;
 
        if (host->platdata && host->platdata->card_readonly)
-               ret = host->platdata->card_readonly(mmc);
-       else
-               ret = 0;        /* assume card is read-write */
+               return !!host->platdata->card_readonly(mmc);
 
-       return ret;
+       return -ENOSYS;
 }
 
 static void au1xmmc_finish_request(struct au1xmmc_host *host)
 {
-
        struct mmc_request *mrq = host->mrq;
 
        host->mrq = NULL;
@@ -216,18 +290,14 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
        au_sync();
 
        /* Wait for the command to go on the line */
-
-       while(1) {
-               if (!(au_readl(HOST_CMD(host)) & SD_CMD_GO))
-                       break;
-       }
+       while (au_readl(HOST_CMD(host)) & SD_CMD_GO)
+               /* nop */;
 
        /* Wait for the command to come back */
-
        if (wait) {
                u32 status = au_readl(HOST_STATUS(host));
 
-               while(!(status & SD_STATUS_CR))
+               while (!(status & SD_STATUS_CR))
                        status = au_readl(HOST_STATUS(host));
 
                /* Clear the CR status */
@@ -241,12 +311,11 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
 
 static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
 {
-
        struct mmc_request *mrq = host->mrq;
        struct mmc_data *data;
        u32 crc;
 
-       WARN_ON(host->status != HOST_S_DATA && host->status != HOST_S_STOP);
+       WARN_ON((host->status != HOST_S_DATA) && (host->status != HOST_S_STOP));
 
        if (host->mrq == NULL)
                return;
@@ -257,15 +326,13 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
                status = au_readl(HOST_STATUS(host));
 
        /* The transaction is really over when the SD_STATUS_DB bit is clear */
-
-       while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
+       while ((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
                status = au_readl(HOST_STATUS(host));
 
        data->error = 0;
        dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
 
         /* Process any errors */
-
        crc = (status & (SD_STATUS_WC | SD_STATUS_RC));
        if (host->flags & HOST_F_XMIT)
                crc |= ((status & 0x07) == 0x02) ? 0 : 1;
@@ -283,15 +350,13 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
 #ifdef CONFIG_SOC_AU1200       /* DBDMA */
                        u32 chan = DMA_CHANNEL(host);
 
-                       chan_tab_t *c = *((chan_tab_t **) chan);
+                       chan_tab_t *c = *((chan_tab_t **)chan);
                        au1x_dma_chan_t *cp = c->chan_ptr;
                        data->bytes_xfered = cp->ddma_bytecnt;
 #endif
-               }
-               else
+               } else
                        data->bytes_xfered =
-                               (data->blocks * data->blksz) -
-                               host->pio.len;
+                               (data->blocks * data->blksz) - host->pio.len;
        }
 
        au1xmmc_finish_request(host);
@@ -299,7 +364,7 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
 
 static void au1xmmc_tasklet_data(unsigned long param)
 {
-       struct au1xmmc_host *host = (struct au1xmmc_host *) param;
+       struct au1xmmc_host *host = (struct au1xmmc_host *)param;
 
        u32 status = au_readl(HOST_STATUS(host));
        au1xmmc_data_complete(host, status);
@@ -309,11 +374,10 @@ static void au1xmmc_tasklet_data(unsigned long param)
 
 static void au1xmmc_send_pio(struct au1xmmc_host *host)
 {
-
-       struct mmc_data *data = 0;
-       int sg_len, max, count = 0;
-       unsigned char *sg_ptr;
-       u32 status = 0;
+       struct mmc_data *data;
+       int sg_len, max, count;
+       unsigned char *sg_ptr, val;
+       u32 status;
        struct scatterlist *sg;
 
        data = host->mrq->data;
@@ -328,14 +392,12 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
        /* This is the space left inside the buffer */
        sg_len = data->sg[host->pio.index].length - host->pio.offset;
 
-       /* Check to if we need less then the size of the sg_buffer */
-
+       /* Check if we need less than the size of the sg_buffer */
        max = (sg_len > host->pio.len) ? host->pio.len : sg_len;
-       if (max > AU1XMMC_MAX_TRANSFER) max = AU1XMMC_MAX_TRANSFER;
-
-       for(count = 0; count < max; count++ ) {
-               unsigned char val;
+       if (max > AU1XMMC_MAX_TRANSFER)
+               max = AU1XMMC_MAX_TRANSFER;
 
+       for (count = 0; count < max; count++) {
                status = au_readl(HOST_STATUS(host));
 
                if (!(status & SD_STATUS_TH))
@@ -343,7 +405,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
 
                val = *sg_ptr++;
 
-               au_writel((unsigned long) val, HOST_TXPORT(host));
+               au_writel((unsigned long)val, HOST_TXPORT(host));
                au_sync();
        }
 
@@ -367,11 +429,10 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
 
 static void au1xmmc_receive_pio(struct au1xmmc_host *host)
 {
-
-       struct mmc_data *data = 0;
-       int sg_len = 0, max = 0, count = 0;
-       unsigned char *sg_ptr = 0;
-       u32 status = 0;
+       struct mmc_data *data;
+       int max, count, sg_len = 0;
+       unsigned char *sg_ptr = NULL;
+       u32 status, val;
        struct scatterlist *sg;
 
        data = host->mrq->data;
@@ -388,15 +449,15 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
                /* This is the space left inside the buffer */
                sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
 
-               /* Check to if we need less then the size of the sg_buffer */
-               if (sg_len < max) max = sg_len;
+               /* Check if we need less than the size of the sg_buffer */
+               if (sg_len < max)
+                       max = sg_len;
        }
 
        if (max > AU1XMMC_MAX_TRANSFER)
                max = AU1XMMC_MAX_TRANSFER;
 
-       for(count = 0; count < max; count++ ) {
-               u32 val;
+       for (count = 0; count < max; count++) {
                status = au_readl(HOST_STATUS(host));
 
                if (!(status & SD_STATUS_NE))
@@ -422,7 +483,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
                val = au_readl(HOST_RXPORT(host));
 
                if (sg_ptr)
-                       *sg_ptr++ = (unsigned char) (val & 0xFF);
+                       *sg_ptr++ = (unsigned char)(val & 0xFF);
        }
 
        host->pio.len -= count;
@@ -434,7 +495,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
        }
 
        if (host->pio.len == 0) {
-               //IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF);
+               /* IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); */
                IRQ_OFF(host, SD_CONFIG_NE);
 
                if (host->flags & HOST_F_STOP)
@@ -444,17 +505,15 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
        }
 }
 
-/* static void au1xmmc_cmd_complete
-   This is called when a command has been completed - grab the response
-   and check for errors.  Then start the data transfer if it is indicated.
-*/
-
+/* This is called when a command has been completed - grab the response
+ * and check for errors.  Then start the data transfer if it is indicated.
+ */
 static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
 {
-
        struct mmc_request *mrq = host->mrq;
        struct mmc_command *cmd;
-       int trans;
+       u32 r[4];
+       int i, trans;
 
        if (!host->mrq)
                return;
@@ -464,9 +523,6 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
 
        if (cmd->flags & MMC_RSP_PRESENT) {
                if (cmd->flags & MMC_RSP_136) {
-                       u32 r[4];
-                       int i;
-
                        r[0] = au_readl(host->iobase + SD_RESP3);
                        r[1] = au_readl(host->iobase + SD_RESP2);
                        r[2] = au_readl(host->iobase + SD_RESP1);
@@ -474,10 +530,9 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
 
                        /* The CRC is omitted from the response, so really
                         * we only got 120 bytes, but the engine expects
-                        * 128 bits, so we have to shift things up
+                        * 128 bits, so we have to shift things up.
                         */
-
-                       for(i = 0; i < 4; i++) {
+                       for (i = 0; i < 4; i++) {
                                cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8;
                                if (i != 3)
                                        cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24;
@@ -488,22 +543,20 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
                         * our response omits the CRC, our data ends up
                         * being shifted 8 bits to the right.  In this case,
                         * that means that the OSR data starts at bit 31,
-                        * so we can just read RESP0 and return that
+                        * so we can just read RESP0 and return that.
                         */
                        cmd->resp[0] = au_readl(host->iobase + SD_RESP0);
                }
        }
 
         /* Figure out errors */
-
        if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
                cmd->error = -EILSEQ;
 
        trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
 
        if (!trans || cmd->error) {
-
-               IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF);
+               IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
                tasklet_schedule(&host->finish_task);
                return;
        }
@@ -530,18 +583,15 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
 
 static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)
 {
-
        unsigned int pbus = get_au1x00_speed();
        unsigned int divisor;
        u32 config;
 
        /* From databook:
-          divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1
-       */
-
+        * divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1
+        */
        pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2);
        pbus /= 2;
-
        divisor = ((pbus / rate) / 2) - 1;
 
        config = au_readl(HOST_CONFIG(host));
@@ -553,8 +603,8 @@ static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)
        au_sync();
 }
 
-static int
-au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
+static int au1xmmc_prepare_data(struct au1xmmc_host *host,
+                               struct mmc_data *data)
 {
        int datalen = data->blocks * data->blksz;
 
@@ -583,7 +633,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
 
                au1xxx_dbdma_stop(channel);
 
-               for(i = 0; i < host->dma.len; i++) {
+               for (i = 0; i < host->dma.len; i++) {
                        u32 ret = 0, flags = DDMA_FLAGS_NOIE;
                        struct scatterlist *sg = &data->sg[i];
                        int sg_len = sg->length;
@@ -593,14 +643,12 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
                        if (i == host->dma.len - 1)
                                flags = DDMA_FLAGS_IE;
 
-                       if (host->flags & HOST_F_XMIT){
-                               ret = au1xxx_dbdma_put_source_flags(channel,
-                                       (void *) sg_virt(sg), len, flags);
-                       }
-                       else {
-                               ret = au1xxx_dbdma_put_dest_flags(channel,
-                                       (void *) sg_virt(sg),
-                                       len, flags);
+                       if (host->flags & HOST_F_XMIT) {
+                               ret = au1xxx_dbdma_put_source_flags(channel,
+                                       (void *)sg_virt(sg), len, flags);
+                       } else {
+                               ret = au1xxx_dbdma_put_dest_flags(channel,
+                                       (void *)sg_virt(sg), len, flags);
                        }
 
                        if (!ret)
@@ -609,8 +657,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
                        datalen -= len;
                }
 #endif
-       }
-       else {
+       } else {
                host->pio.index = 0;
                host->pio.offset = 0;
                host->pio.len = datalen;
@@ -619,7 +666,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
                        IRQ_ON(host, SD_CONFIG_TH);
                else
                        IRQ_ON(host, SD_CONFIG_NE);
-                       //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);
+                       /* IRQ_ON(host, SD_CONFIG_RA | SD_CONFIG_RF); */
        }
 
        return 0;
@@ -630,15 +677,10 @@ dataerr:
        return -ETIMEDOUT;
 }
 
-/* static void au1xmmc_request
-   This actually starts a command or data transaction
-*/
-
+/* This actually starts a command or data transaction */
 static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
 {
-
        struct au1xmmc_host *host = mmc_priv(mmc);
-       unsigned int flags = 0;
        int ret = 0;
 
        WARN_ON(irqs_disabled());
@@ -647,9 +689,15 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
        host->mrq = mrq;
        host->status = HOST_S_CMD;
 
+       /* fail request immediately if no card is present */
+       if (0 == au1xmmc_card_inserted(mmc)) {
+               mrq->cmd->error = -ENOMEDIUM;
+               au1xmmc_finish_request(host);
+               return;
+       }
+
        if (mrq->data) {
                FLUSH_FIFO(host);
-               flags = mrq->data->flags;
                ret = au1xmmc_prepare_data(host, mrq->data);
        }
 
@@ -664,7 +712,6 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
 
 static void au1xmmc_reset_controller(struct au1xmmc_host *host)
 {
-
        /* Apply the clock */
        au_writel(SD_ENABLE_CE, HOST_ENABLE(host));
         au_sync_delay(1);
@@ -694,9 +741,10 @@ static void au1xmmc_reset_controller(struct au1xmmc_host *host)
 }
 
 
-static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
+static void au1xmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct au1xmmc_host *host = mmc_priv(mmc);
+       u32 config2;
 
        if (ios->power_mode == MMC_POWER_OFF)
                au1xmmc_set_power(host, 0);
@@ -708,6 +756,18 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
                au1xmmc_set_clock(host, ios->clock);
                host->clock = ios->clock;
        }
+
+       config2 = au_readl(HOST_CONFIG2(host));
+       switch (ios->bus_width) {
+       case MMC_BUS_WIDTH_4:
+               config2 |= SD_CONFIG2_WB;
+               break;
+       case MMC_BUS_WIDTH_1:
+               config2 &= ~SD_CONFIG2_WB;
+               break;
+       }
+       au_writel(config2, HOST_CONFIG2(host));
+       au_sync();
 }
 
 #define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT)
@@ -724,6 +784,9 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
        if (!(status & SD_STATUS_I))
                return IRQ_NONE;        /* not ours */
 
+       if (status & SD_STATUS_SI)      /* SDIO */
+               mmc_signal_sdio_irq(host->mmc);
+
        if (host->mrq && (status & STATUS_TIMEOUT)) {
                if (status & SD_STATUS_RAT)
                        host->mrq->cmd->error = -ETIMEDOUT;
@@ -850,42 +913,24 @@ static void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host)
 }
 #endif
 
+static void au1xmmc_enable_sdio_irq(struct mmc_host *mmc, int en)
+{
+       struct au1xmmc_host *host = mmc_priv(mmc);
+
+       if (en)
+               IRQ_ON(host, SD_CONFIG_SI);
+       else
+               IRQ_OFF(host, SD_CONFIG_SI);
+}
+
 static const struct mmc_host_ops au1xmmc_ops = {
        .request        = au1xmmc_request,
        .set_ios        = au1xmmc_set_ios,
        .get_ro         = au1xmmc_card_readonly,
+       .get_cd         = au1xmmc_card_inserted,
+       .enable_sdio_irq = au1xmmc_enable_sdio_irq,
 };
 
-static void au1xmmc_poll_event(unsigned long arg)
-{
-       struct au1xmmc_host *host = (struct au1xmmc_host *)arg;
-       int card = au1xmmc_card_inserted(host);
-       int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0;
-
-       if (card != controller) {
-               host->flags &= ~HOST_F_ACTIVE;
-               if (card)
-                       host->flags |= HOST_F_ACTIVE;
-               mmc_detect_change(host->mmc, 0);
-       }
-
-#ifdef DEBUG
-       if (host->mrq != NULL) {
-               u32 status = au_readl(HOST_STATUS(host));
-               DBG("PENDING - %8.8x\n", host->pdev->id, status);
-       }
-#endif
-       mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT);
-}
-
-static void au1xmmc_init_cd_poll_timer(struct au1xmmc_host *host)
-{
-       init_timer(&host->timer);
-       host->timer.function = au1xmmc_poll_event;
-       host->timer.data = (unsigned long)host;
-       host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT;
-}
-
 static int __devinit au1xmmc_probe(struct platform_device *pdev)
 {
        struct mmc_host *mmc;
@@ -952,7 +997,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
        mmc->max_blk_count = 512;
 
        mmc->ocr_avail = AU1XMMC_OCR;
-       mmc->caps = 0;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
 
        host->status = HOST_S_IDLE;
 
@@ -960,13 +1005,11 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
        if (host->platdata && host->platdata->cd_setup) {
                ret = host->platdata->cd_setup(mmc, 1);
                if (ret) {
-                       dev_err(&pdev->dev, "board CD setup failed\n");
-                       goto out4;
+                       dev_warn(&pdev->dev, "board CD setup failed\n");
+                       mmc->caps |= MMC_CAP_NEEDS_POLL;
                }
-       } else {
-               /* poll the board-specific is-card-in-socket-? method */
-               au1xmmc_init_cd_poll_timer(host);
-       }
+       } else
+               mmc->caps |= MMC_CAP_NEEDS_POLL;
 
        tasklet_init(&host->data_task, au1xmmc_tasklet_data,
                        (unsigned long)host);
@@ -1000,11 +1043,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
                goto out6;
        }
 
-       platform_set_drvdata(pdev, mmc);
-
-       /* start the carddetect poll timer if necessary */
-       if (!(host->platdata && host->platdata->cd_setup))
-               add_timer(&host->timer);
+       platform_set_drvdata(pdev, host);
 
        printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
                " (mode=%s)\n", pdev->id, host->iobase,
@@ -1030,9 +1069,10 @@ out5:
        tasklet_kill(&host->data_task);
        tasklet_kill(&host->finish_task);
 
-       if (host->platdata && host->platdata->cd_setup)
+       if (host->platdata && host->platdata->cd_setup &&
+           !(mmc->caps & MMC_CAP_NEEDS_POLL))
                host->platdata->cd_setup(mmc, 0);
-out4:
+
        free_irq(host->irq, host);
 out3:
        iounmap((void *)host->iobase);
@@ -1047,23 +1087,19 @@ out0:
 
 static int __devexit au1xmmc_remove(struct platform_device *pdev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(pdev);
-       struct au1xmmc_host *host;
-
-       if (mmc) {
-               host  = mmc_priv(mmc);
+       struct au1xmmc_host *host = platform_get_drvdata(pdev);
 
-               mmc_remove_host(mmc);
+       if (host) {
+               mmc_remove_host(host->mmc);
 
 #ifdef CONFIG_LEDS_CLASS
                if (host->platdata && host->platdata->led)
                        led_classdev_unregister(host->platdata->led);
 #endif
 
-               if (host->platdata && host->platdata->cd_setup)
-                       host->platdata->cd_setup(mmc, 0);
-               else
-                       del_timer_sync(&host->timer);
+               if (host->platdata && host->platdata->cd_setup &&
+                   !(host->mmc->caps & MMC_CAP_NEEDS_POLL))
+                       host->platdata->cd_setup(host->mmc, 0);
 
                au_writel(0, HOST_ENABLE(host));
                au_writel(0, HOST_CONFIG(host));
@@ -1083,16 +1119,49 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
                release_resource(host->ioarea);
                kfree(host->ioarea);
 
-               mmc_free_host(mmc);
+               mmc_free_host(host->mmc);
+               platform_set_drvdata(pdev, NULL);
        }
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct au1xmmc_host *host = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = mmc_suspend_host(host->mmc, state);
+       if (ret)
+               return ret;
+
+       au_writel(0, HOST_CONFIG2(host));
+       au_writel(0, HOST_CONFIG(host));
+       au_writel(0xffffffff, HOST_STATUS(host));
+       au_writel(0, HOST_ENABLE(host));
+       au_sync();
+
+       return 0;
+}
+
+static int au1xmmc_resume(struct platform_device *pdev)
+{
+       struct au1xmmc_host *host = platform_get_drvdata(pdev);
+
+       au1xmmc_reset_controller(host);
+
+       return mmc_resume_host(host->mmc);
+}
+#else
+#define au1xmmc_suspend NULL
+#define au1xmmc_resume NULL
+#endif
+
 static struct platform_driver au1xmmc_driver = {
        .probe         = au1xmmc_probe,
        .remove        = au1xmmc_remove,
-       .suspend       = NULL,
-       .resume        = NULL,
+       .suspend       = au1xmmc_suspend,
+       .resume        = au1xmmc_resume,
        .driver        = {
                .name  = DRIVER_NAME,
                .owner = THIS_MODULE,