Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / mmc / wbsd.c
index b7fbd30..8167332 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include "wbsd.h"
 
 #define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.2"
+#define DRIVER_VERSION "1.5"
 
-#ifdef CONFIG_MMC_DEBUG
 #define DBG(x...) \
-       printk(KERN_DEBUG DRIVER_NAME ": " x)
+       pr_debug(DRIVER_NAME ": " x)
 #define DBGF(f, x...) \
-       printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__ , ##x)
-#else
-#define DBG(x...)      do { } while (0)
-#define DBGF(x...)     do { } while (0)
-#endif
-
-#ifdef CONFIG_MMC_DEBUG
-void DBG_REG(int reg, u8 value)
-{
-       int i;
-       
-       printk(KERN_DEBUG "wbsd: Register %d: 0x%02X %3d '%c' ",
-               reg, (int)value, (int)value, (value < 0x20)?'.':value);
-       
-       for (i = 7;i >= 0;i--)
-       {
-               if (value & (1 << i))
-                       printk("x");
-               else
-                       printk(".");
-       }
-       
-       printk("\n");
-}
-#else
-#define DBG_REG(r, v) do {}  while (0)
-#endif
+       pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
 
 /*
  * Device resources
@@ -92,6 +65,13 @@ MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
 
 #endif /* CONFIG_PNP */
 
+static const int config_ports[] = { 0x2E, 0x4E };
+static const int unlock_codes[] = { 0x83, 0x87 };
+
+static const int valid_ids[] = {
+       0x7112,
+       };
+
 #ifdef CONFIG_PNP
 static unsigned int nopnp = 0;
 #else
@@ -105,44 +85,44 @@ static int dma = 2;
  * Basic functions
  */
 
-static inline void wbsd_unlock_config(struct wbsd_hosthost)
+static inline void wbsd_unlock_config(struct wbsd_host *host)
 {
        BUG_ON(host->config == 0);
-       
+
        outb(host->unlock_code, host->config);
        outb(host->unlock_code, host->config);
 }
 
-static inline void wbsd_lock_config(struct wbsd_hosthost)
+static inline void wbsd_lock_config(struct wbsd_host *host)
 {
        BUG_ON(host->config == 0);
-       
+
        outb(LOCK_CODE, host->config);
 }
 
-static inline void wbsd_write_config(struct wbsd_hosthost, u8 reg, u8 value)
+static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value)
 {
        BUG_ON(host->config == 0);
-       
+
        outb(reg, host->config);
        outb(value, host->config + 1);
 }
 
-static inline u8 wbsd_read_config(struct wbsd_hosthost, u8 reg)
+static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg)
 {
        BUG_ON(host->config == 0);
-       
+
        outb(reg, host->config);
        return inb(host->config + 1);
 }
 
-static inline void wbsd_write_index(struct wbsd_hosthost, u8 index, u8 value)
+static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
 {
        outb(index, host->base + WBSD_IDXR);
        outb(value, host->base + WBSD_DATAR);
 }
 
-static inline u8 wbsd_read_index(struct wbsd_hosthost, u8 index)
+static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
 {
        outb(index, host->base + WBSD_IDXR);
        return inb(host->base + WBSD_DATAR);
@@ -152,24 +132,24 @@ static inline u8 wbsd_read_index(struct wbsd_host* host, u8 index)
  * Common routines
  */
 
-static void wbsd_init_device(struct wbsd_hosthost)
+static void wbsd_init_device(struct wbsd_host *host)
 {
        u8 setup, ier;
-       
+
        /*
         * Reset chip (SD/MMC part) and fifo.
         */
        setup = wbsd_read_index(host, WBSD_IDX_SETUP);
        setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
        wbsd_write_index(host, WBSD_IDX_SETUP, setup);
-       
+
        /*
         * Set DAT3 to input
         */
        setup &= ~WBSD_DAT3_H;
        wbsd_write_index(host, WBSD_IDX_SETUP, setup);
        host->flags &= ~WBSD_FIGNORE_DETECT;
-       
+
        /*
         * Read back default clock.
         */
@@ -179,12 +159,12 @@ static void wbsd_init_device(struct wbsd_host* host)
         * Power down port.
         */
        outb(WBSD_POWER_N, host->base + WBSD_CSR);
-       
+
        /*
         * Set maximum timeout.
         */
        wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
-       
+
        /*
         * Test for card presence
         */
@@ -192,7 +172,7 @@ static void wbsd_init_device(struct wbsd_host* host)
                host->flags |= WBSD_FCARD_PRESENT;
        else
                host->flags &= ~WBSD_FCARD_PRESENT;
-       
+
        /*
         * Enable interesting interrupts.
         */
@@ -212,12 +192,12 @@ static void wbsd_init_device(struct wbsd_host* host)
        inb(host->base + WBSD_ISR);
 }
 
-static void wbsd_reset(struct wbsd_hosthost)
+static void wbsd_reset(struct wbsd_host *host)
 {
        u8 setup;
-       
-       printk(KERN_ERR DRIVER_NAME ": Resetting chip\n");
-       
+
+       printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
+
        /*
         * Soft reset of chip (SD/MMC part).
         */
@@ -226,14 +206,13 @@ static void wbsd_reset(struct wbsd_host* host)
        wbsd_write_index(host, WBSD_IDX_SETUP, setup);
 }
 
-static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq)
+static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
 {
        unsigned long dmaflags;
-       
+
        DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
-       
-       if (host->dma >= 0)
-       {
+
+       if (host->dma >= 0) {
                /*
                 * Release ISA DMA controller.
                 */
@@ -247,7 +226,7 @@ static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq)
                 */
                wbsd_write_index(host, WBSD_IDX_DMA, 0);
        }
-       
+
        host->mrq = NULL;
 
        /*
@@ -262,7 +241,7 @@ static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq)
  * Scatter/gather functions
  */
 
-static inline void wbsd_init_sg(struct wbsd_host* host, struct mmc_data* data)
+static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
 {
        /*
         * Get info. about SG list from data structure.
@@ -274,7 +253,7 @@ static inline void wbsd_init_sg(struct wbsd_host* host, struct mmc_data* data)
        host->remain = host->cur_sg->length;
 }
 
-static inline int wbsd_next_sg(struct wbsd_hosthost)
+static inline int wbsd_next_sg(struct wbsd_host *host)
 {
        /*
         * Skip to next SG entry.
@@ -285,46 +264,44 @@ static inline int wbsd_next_sg(struct wbsd_host* host)
        /*
         * Any entries left?
         */
-       if (host->num_sg > 0)
-         {
-           host->offset = 0;
-           host->remain = host->cur_sg->length;
-         }
-       
+       if (host->num_sg > 0) {
+               host->offset = 0;
+               host->remain = host->cur_sg->length;
+       }
+
        return host->num_sg;
 }
 
-static inline char* wbsd_kmap_sg(struct wbsd_host* host)
+static inline char *wbsd_kmap_sg(struct wbsd_host *host)
 {
        host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
                host->cur_sg->offset;
        return host->mapped_sg;
 }
 
-static inline void wbsd_kunmap_sg(struct wbsd_hosthost)
+static inline void wbsd_kunmap_sg(struct wbsd_host *host)
 {
        kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
 }
 
-static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data)
+static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
 {
        unsigned int len, i, size;
-       struct scatterlistsg;
-       chardmabuf = host->dma_buffer;
-       charsgbuf;
-       
+       struct scatterlist *sg;
+       char *dmabuf = host->dma_buffer;
+       char *sgbuf;
+
        size = host->size;
-       
+
        sg = data->sg;
        len = data->sg_len;
-       
+
        /*
         * Just loop through all entries. Size might not
         * be the entire list though so make sure that
         * we do not transfer too much.
         */
-       for (i = 0;i < len;i++)
-       {
+       for (i = 0; i < len; i++) {
                sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
                if (size < sg[i].length)
                        memcpy(dmabuf, sgbuf, size);
@@ -332,45 +309,44 @@ static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data)
                        memcpy(dmabuf, sgbuf, sg[i].length);
                kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
                dmabuf += sg[i].length;
-               
+
                if (size < sg[i].length)
                        size = 0;
                else
                        size -= sg[i].length;
-       
+
                if (size == 0)
                        break;
        }
-       
+
        /*
         * Check that we didn't get a request to transfer
         * more data than can fit into the SG list.
         */
-       
+
        BUG_ON(size != 0);
-       
+
        host->size -= size;
 }
 
-static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data)
+static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
 {
        unsigned int len, i, size;
-       struct scatterlistsg;
-       chardmabuf = host->dma_buffer;
-       charsgbuf;
-       
+       struct scatterlist *sg;
+       char *dmabuf = host->dma_buffer;
+       char *sgbuf;
+
        size = host->size;
-       
+
        sg = data->sg;
        len = data->sg_len;
-       
+
        /*
         * Just loop through all entries. Size might not
         * be the entire list though so make sure that
         * we do not transfer too much.
         */
-       for (i = 0;i < len;i++)
-       {
+       for (i = 0; i < len; i++) {
                sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
                if (size < sg[i].length)
                        memcpy(sgbuf, dmabuf, size);
@@ -378,70 +354,62 @@ static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data)
                        memcpy(sgbuf, dmabuf, sg[i].length);
                kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
                dmabuf += sg[i].length;
-               
+
                if (size < sg[i].length)
                        size = 0;
                else
                        size -= sg[i].length;
-               
+
                if (size == 0)
                        break;
        }
-       
+
        /*
         * Check that we didn't get a request to transfer
         * more data than can fit into the SG list.
         */
-       
+
        BUG_ON(size != 0);
-       
+
        host->size -= size;
 }
 
 /*
  * Command handling
  */
-static inline void wbsd_get_short_reply(struct wbsd_hosthost,
-       struct mmc_command* cmd)
+
+static inline void wbsd_get_short_reply(struct wbsd_host *host,
+                                       struct mmc_command *cmd)
 {
        /*
         * Correct response type?
         */
-       if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT)
-       {
+       if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
                cmd->error = MMC_ERR_INVALID;
                return;
        }
-       
-       cmd->resp[0] =
-               wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
-       cmd->resp[0] |=
-               wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
-       cmd->resp[0] |=
-               wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
-       cmd->resp[0] |=
-               wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
-       cmd->resp[1] =
-               wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
+
+       cmd->resp[0]  = wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
+       cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
+       cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
+       cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
+       cmd->resp[1]  = wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
 }
 
-static inline void wbsd_get_long_reply(struct wbsd_hosthost,
-       struct mmc_commandcmd)
+static inline void wbsd_get_long_reply(struct wbsd_host *host,
+       struct mmc_command *cmd)
 {
        int i;
-       
+
        /*
         * Correct response type?
         */
-       if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG)
-       {
+       if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
                cmd->error = MMC_ERR_INVALID;
                return;
        }
-       
-       for (i = 0;i < 4;i++)
-       {
+
+       for (i = 0; i < 4; i++) {
                cmd->resp[i] =
                        wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24;
                cmd->resp[i] |=
@@ -453,11 +421,11 @@ static inline void wbsd_get_long_reply(struct wbsd_host* host,
        }
 }
 
-static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
+static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
 {
        int i;
        u8 status, isr;
-       
+
        DBGF("Sending cmd (%x)\n", cmd->opcode);
 
        /*
@@ -466,16 +434,16 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
         * transfer.
         */
        host->isr = 0;
-       
+
        /*
         * Send the command (CRC calculated by host).
         */
        outb(cmd->opcode, host->base + WBSD_CMDR);
-       for (i = 3;i >= 0;i--)
+       for (i = 3; i >= 0; i--)
                outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
-       
+
        cmd->error = MMC_ERR_NONE;
-       
+
        /*
         * Wait for the request to complete.
         */
@@ -486,13 +454,12 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
        /*
         * Do we expect a reply?
         */
-       if ((cmd->flags & MMC_RSP_MASK) != MMC_RSP_NONE)
-       {
+       if (cmd->flags & MMC_RSP_PRESENT) {
                /*
                 * Read back status.
                 */
                isr = host->isr;
-               
+
                /* Card removed? */
                if (isr & WBSD_INT_CARD)
                        cmd->error = MMC_ERR_TIMEOUT;
@@ -503,12 +470,11 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
                else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
                        cmd->error = MMC_ERR_BADCRC;
                /* All ok */
-               else
-               {
-                       if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT)
-                               wbsd_get_short_reply(host, cmd);
-                       else
+               else {
+                       if (cmd->flags & MMC_RSP_136)
                                wbsd_get_long_reply(host, cmd);
+                       else
+                               wbsd_get_short_reply(host, cmd);
                }
        }
 
@@ -519,67 +485,62 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
  * Data functions
  */
 
-static void wbsd_empty_fifo(struct wbsd_hosthost)
+static void wbsd_empty_fifo(struct wbsd_host *host)
 {
-       struct mmc_datadata = host->mrq->cmd->data;
-       charbuffer;
+       struct mmc_data *data = host->mrq->cmd->data;
+       char *buffer;
        int i, fsr, fifo;
-       
+
        /*
         * Handle excessive data.
         */
        if (data->bytes_xfered == host->size)
                return;
-       
+
        buffer = wbsd_kmap_sg(host) + host->offset;
 
        /*
         * Drain the fifo. This has a tendency to loop longer
         * than the FIFO length (usually one block).
         */
-       while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY))
-       {
+       while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
                /*
                 * The size field in the FSR is broken so we have to
                 * do some guessing.
-                */             
+                */
                if (fsr & WBSD_FIFO_FULL)
                        fifo = 16;
                else if (fsr & WBSD_FIFO_FUTHRE)
                        fifo = 8;
                else
                        fifo = 1;
-               
-               for (i = 0;i < fifo;i++)
-               {
+
+               for (i = 0; i < fifo; i++) {
                        *buffer = inb(host->base + WBSD_DFR);
                        buffer++;
                        host->offset++;
                        host->remain--;
 
                        data->bytes_xfered++;
-                       
+
                        /*
                         * Transfer done?
                         */
-                       if (data->bytes_xfered == host->size)
-                       {
-                               wbsd_kunmap_sg(host);                           
+                       if (data->bytes_xfered == host->size) {
+                               wbsd_kunmap_sg(host);
                                return;
                        }
-                       
+
                        /*
                         * End of scatter list entry?
                         */
-                       if (host->remain == 0)
-                       {
+                       if (host->remain == 0) {
                                wbsd_kunmap_sg(host);
-                               
+
                                /*
                                 * Get next entry. Check if last.
                                 */
-                               if (!wbsd_next_sg(host))
-                               {
+                               if (!wbsd_next_sg(host)) {
                                        /*
                                         * We should never reach this point.
                                         * It means that we're trying to
@@ -587,17 +548,17 @@ static void wbsd_empty_fifo(struct wbsd_host* host)
                                         * into the scatter list.
                                         */
                                        BUG_ON(1);
-                                       
+
                                        host->size = data->bytes_xfered;
-                                       
+
                                        return;
                                }
-                               
+
                                buffer = wbsd_kmap_sg(host);
                        }
                }
        }
-       
+
        wbsd_kunmap_sg(host);
 
        /*
@@ -609,12 +570,12 @@ static void wbsd_empty_fifo(struct wbsd_host* host)
                tasklet_schedule(&host->fifo_tasklet);
 }
 
-static void wbsd_fill_fifo(struct wbsd_hosthost)
+static void wbsd_fill_fifo(struct wbsd_host *host)
 {
-       struct mmc_datadata = host->mrq->cmd->data;
-       charbuffer;
+       struct mmc_data *data = host->mrq->cmd->data;
+       char *buffer;
        int i, fsr, fifo;
-       
+
        /*
         * Check that we aren't being called after the
         * entire buffer has been transfered.
@@ -628,12 +589,11 @@ static void wbsd_fill_fifo(struct wbsd_host* host)
         * Fill the fifo. This has a tendency to loop longer
         * than the FIFO length (usually one block).
         */
-       while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL))
-       {
+       while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) {
                /*
                 * The size field in the FSR is broken so we have to
                 * do some guessing.
-                */             
+                */
                if (fsr & WBSD_FIFO_EMPTY)
                        fifo = 0;
                else if (fsr & WBSD_FIFO_EMTHRE)
@@ -641,20 +601,18 @@ static void wbsd_fill_fifo(struct wbsd_host* host)
                else
                        fifo = 15;
 
-               for (i = 16;i > fifo;i--)
-               {
+               for (i = 16; i > fifo; i--) {
                        outb(*buffer, host->base + WBSD_DFR);
                        buffer++;
                        host->offset++;
                        host->remain--;
-                       
+
                        data->bytes_xfered++;
-                       
+
                        /*
                         * Transfer done?
                         */
-                       if (data->bytes_xfered == host->size)
-                       {
+                       if (data->bytes_xfered == host->size) {
                                wbsd_kunmap_sg(host);
                                return;
                        }
@@ -662,15 +620,13 @@ static void wbsd_fill_fifo(struct wbsd_host* host)
                        /*
                         * End of scatter list entry?
                         */
-                       if (host->remain == 0)
-                       {
+                       if (host->remain == 0) {
                                wbsd_kunmap_sg(host);
-                               
+
                                /*
                                 * Get next entry. Check if last.
                                 */
-                               if (!wbsd_next_sg(host))
-                               {
+                               if (!wbsd_next_sg(host)) {
                                        /*
                                         * We should never reach this point.
                                         * It means that we're trying to
@@ -678,19 +634,19 @@ static void wbsd_fill_fifo(struct wbsd_host* host)
                                         * into the scatter list.
                                         */
                                        BUG_ON(1);
-                                       
+
                                        host->size = data->bytes_xfered;
-                                       
+
                                        return;
                                }
-                               
+
                                buffer = wbsd_kmap_sg(host);
                        }
                }
        }
-       
+
        wbsd_kunmap_sg(host);
-       
+
        /*
         * The controller stops sending interrupts for
         * 'FIFO empty' under certain conditions. So we
@@ -699,21 +655,21 @@ static void wbsd_fill_fifo(struct wbsd_host* host)
        tasklet_schedule(&host->fifo_tasklet);
 }
 
-static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
+static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
 {
        u16 blksize;
        u8 setup;
        unsigned long dmaflags;
 
        DBGF("blksz %04x blks %04x flags %08x\n",
-               1 << data->blksz_bits, data->blocks, data->flags);
+               data->blksz, data->blocks, data->flags);
        DBGF("tsac %d ms nsac %d clk\n",
                data->timeout_ns / 1000000, data->timeout_clks);
-       
+
        /*
         * Calculate size.
         */
-       host->size = data->blocks << data->blksz_bits;
+       host->size = data->blocks * data->blksz;
 
        /*
         * Check timeout values for overflow.
@@ -721,25 +677,39 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
         */
        if (data->timeout_ns > 127000000)
                wbsd_write_index(host, WBSD_IDX_TAAC, 127);
-       else
-               wbsd_write_index(host, WBSD_IDX_TAAC, data->timeout_ns/1000000);
-       
+       else {
+               wbsd_write_index(host, WBSD_IDX_TAAC,
+                       data->timeout_ns / 1000000);
+       }
+
        if (data->timeout_clks > 255)
                wbsd_write_index(host, WBSD_IDX_NSAC, 255);
        else
                wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
-       
+
        /*
         * Inform the chip of how large blocks will be
         * sent. It needs this to determine when to
         * calculate CRC.
         *
         * Space for CRC must be included in the size.
+        * Two bytes are needed for each data line.
         */
-       blksize = (1 << data->blksz_bits) + 2;
-       
-       wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
-       wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+       if (host->bus_width == MMC_BUS_WIDTH_1) {
+               blksize = data->blksz + 2;
+
+               wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
+               wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+       } else if (host->bus_width == MMC_BUS_WIDTH_4) {
+               blksize = data->blksz + 2 * 4;
+
+               wbsd_write_index(host, WBSD_IDX_PBSMSB,
+                       ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
+               wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+       } else {
+               data->error = MMC_ERR_INVALID;
+               return;
+       }
 
        /*
         * Clear the FIFO. This is needed even for DMA
@@ -749,32 +719,30 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
        setup = wbsd_read_index(host, WBSD_IDX_SETUP);
        setup |= WBSD_FIFO_RESET;
        wbsd_write_index(host, WBSD_IDX_SETUP, setup);
-       
+
        /*
         * DMA transfer?
         */
-       if (host->dma >= 0)
-       {       
+       if (host->dma >= 0) {
                /*
                 * The buffer for DMA is only 64 kB.
                 */
                BUG_ON(host->size > 0x10000);
-               if (host->size > 0x10000)
-               {
+               if (host->size > 0x10000) {
                        data->error = MMC_ERR_INVALID;
                        return;
                }
-               
+
                /*
                 * Transfer data from the SG list to
                 * the DMA buffer.
                 */
                if (data->flags & MMC_DATA_WRITE)
                        wbsd_sg_to_dma(host, data);
-               
+
                /*
                 * Initialise the ISA DMA controller.
-                */     
+                */
                dmaflags = claim_dma_lock();
                disable_dma(host->dma);
                clear_dma_ff(host->dma);
@@ -792,51 +760,46 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
                 * Enable DMA on the host.
                 */
                wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
-       }
-       else
-       {
+       } else {
                /*
                 * This flag is used to keep printk
                 * output to a minimum.
                 */
                host->firsterr = 1;
-               
+
                /*
                 * Initialise the SG list.
                 */
                wbsd_init_sg(host, data);
-       
+
                /*
                 * Turn off DMA.
                 */
                wbsd_write_index(host, WBSD_IDX_DMA, 0);
-       
+
                /*
                 * Set up FIFO threshold levels (and fill
                 * buffer if doing a write).
                 */
-               if (data->flags & MMC_DATA_READ)
-               {
+               if (data->flags & MMC_DATA_READ) {
                        wbsd_write_index(host, WBSD_IDX_FIFOEN,
                                WBSD_FIFOEN_FULL | 8);
-               }
-               else
-               {
+               } else {
                        wbsd_write_index(host, WBSD_IDX_FIFOEN,
                                WBSD_FIFOEN_EMPTY | 8);
                        wbsd_fill_fifo(host);
                }
-       }       
-               
+       }
+
        data->error = MMC_ERR_NONE;
 }
 
-static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data)
+static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
 {
        unsigned long dmaflags;
        int count;
        u8 status;
-       
+
        WARN_ON(host->mrq == NULL);
 
        /*
@@ -849,21 +812,19 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data)
         * Wait for the controller to leave data
         * transfer state.
         */
-       do
-       {
+       do {
                status = wbsd_read_index(host, WBSD_IDX_STATUS);
        } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
-       
+
        /*
         * DMA transfer?
         */
-       if (host->dma >= 0)
-       {
+       if (host->dma >= 0) {
                /*
                 * Disable DMA on the host.
                 */
                wbsd_write_index(host, WBSD_IDX_DMA, 0);
-               
+
                /*
                 * Turn of ISA DMA controller.
                 */
@@ -872,32 +833,30 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data)
                clear_dma_ff(host->dma);
                count = get_dma_residue(host->dma);
                release_dma_lock(dmaflags);
-               
+
                /*
                 * Any leftover data?
                 */
-               if (count)
-               {
-                       printk(KERN_ERR DRIVER_NAME ": Incomplete DMA "
-                               "transfer. %d bytes left.\n", count);
-                       
+               if (count) {
+                       printk(KERN_ERR "%s: Incomplete DMA transfer. "
+                               "%d bytes left.\n",
+                               mmc_hostname(host->mmc), count);
+
                        data->error = MMC_ERR_FAILED;
-               }
-               else
-               {
+               } else {
                        /*
                         * Transfer data from DMA buffer to
                         * SG list.
                         */
                        if (data->flags & MMC_DATA_READ)
                                wbsd_dma_to_sg(host, data);
-                       
+
                        data->bytes_xfered = host->size;
                }
        }
-       
+
        DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
-       
+
        wbsd_request_end(host, host->mrq);
 }
 
@@ -907,10 +866,10 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data)
  *                                                                           *
 \*****************************************************************************/
 
-static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
+static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
-       struct wbsd_hosthost = mmc_priv(mmc);
-       struct mmc_commandcmd;
+       struct wbsd_host *host = mmc_priv(mmc);
+       struct mmc_command *cmd;
 
        /*
         * Disable tasklets to avoid a deadlock.
@@ -922,13 +881,12 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
        cmd = mrq->cmd;
 
        host->mrq = mrq;
-       
+
        /*
         * If there is no card in the slot then
         * timeout immediatly.
         */
-       if (!(host->flags & WBSD_FCARD_PRESENT))
-       {
+       if (!(host->flags & WBSD_FCARD_PRESENT)) {
                cmd->error = MMC_ERR_TIMEOUT;
                goto done;
        }
@@ -936,23 +894,21 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
        /*
         * Does the request include data?
         */
-       if (cmd->data)
-       {
+       if (cmd->data) {
                wbsd_prepare_data(host, cmd->data);
-               
+
                if (cmd->data->error != MMC_ERR_NONE)
                        goto done;
        }
-       
+
        wbsd_send_command(host, cmd);
 
        /*
         * If this is a data transfer the request
         * will be finished after the data has
         * transfered.
-        */     
-       if (cmd->data && (cmd->error == MMC_ERR_NONE))
-       {
+        */
+       if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
                /*
                 * Dirty fix for hardware bug.
                 */
@@ -963,20 +919,17 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
 
                return;
        }
-               
+
 done:
        wbsd_request_end(host, mrq);
 
        spin_unlock_bh(&host->lock);
 }
 
-static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
+static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
-       struct wbsd_hosthost = mmc_priv(mmc);
+       struct wbsd_host *host = mmc_priv(mmc);
        u8 clk, setup, pwr;
-       
-       DBGF("clock %uHz busmode %u powermode %u Vdd %u\n",
-               ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);
 
        spin_lock_bh(&host->lock);
 
@@ -986,7 +939,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
         */
        if (ios->power_mode == MMC_POWER_OFF)
                wbsd_init_device(host);
-       
+
        if (ios->clock >= 24000000)
                clk = WBSD_CLK_24M;
        else if (ios->clock >= 16000000)
@@ -1000,8 +953,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
         * Only write to the clock register when
         * there is an actual change.
         */
-       if (clk != host->clk)
-       {
+       if (clk != host->clk) {
                wbsd_write_index(host, WBSD_IDX_CLK, clk);
                host->clk = clk;
        }
@@ -1009,8 +961,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
        /*
         * Power up card.
         */
-       if (ios->power_mode != MMC_POWER_OFF)
-       {
+       if (ios->power_mode != MMC_POWER_OFF) {
                pwr = inb(host->base + WBSD_CSR);
                pwr &= ~WBSD_POWER_N;
                outb(pwr, host->base + WBSD_CSR);
@@ -1018,30 +969,62 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
 
        /*
         * MMC cards need to have pin 1 high during init.
-        * Init time corresponds rather nicely with the bus mode.
         * It wreaks havoc with the card detection though so
-        * that needs to be disabed.
+        * that needs to be disabled.
         */
        setup = wbsd_read_index(host, WBSD_IDX_SETUP);
-       if ((ios->power_mode == MMC_POWER_ON) &&
-               (ios->bus_mode == MMC_BUSMODE_OPENDRAIN))
-       {
+       if (ios->chip_select == MMC_CS_HIGH) {
+               BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
                setup |= WBSD_DAT3_H;
                host->flags |= WBSD_FIGNORE_DETECT;
-       }
-       else
-       {
-               setup &= ~WBSD_DAT3_H;
-               host->flags &= ~WBSD_FIGNORE_DETECT;
+       } else {
+               if (setup & WBSD_DAT3_H) {
+                       setup &= ~WBSD_DAT3_H;
+
+                       /*
+                        * We cannot resume card detection immediatly
+                        * because of capacitance and delays in the chip.
+                        */
+                       mod_timer(&host->ignore_timer, jiffies + HZ / 100);
+               }
        }
        wbsd_write_index(host, WBSD_IDX_SETUP, setup);
-       
+
+       /*
+        * Store bus width for later. Will be used when
+        * setting up the data transfer.
+        */
+       host->bus_width = ios->bus_width;
+
        spin_unlock_bh(&host->lock);
 }
 
+static int wbsd_get_ro(struct mmc_host *mmc)
+{
+       struct wbsd_host *host = mmc_priv(mmc);
+       u8 csr;
+
+       spin_lock_bh(&host->lock);
+
+       csr = inb(host->base + WBSD_CSR);
+       csr |= WBSD_MSLED;
+       outb(csr, host->base + WBSD_CSR);
+
+       mdelay(1);
+
+       csr = inb(host->base + WBSD_CSR);
+       csr &= ~WBSD_MSLED;
+       outb(csr, host->base + WBSD_CSR);
+
+       spin_unlock_bh(&host->lock);
+
+       return csr & WBSD_WRPT;
+}
+
 static struct mmc_host_ops wbsd_ops = {
        .request        = wbsd_request,
        .set_ios        = wbsd_set_ios,
+       .get_ro         = wbsd_get_ro,
 };
 
 /*****************************************************************************\
@@ -1050,11 +1033,36 @@ static struct mmc_host_ops wbsd_ops = {
  *                                                                           *
 \*****************************************************************************/
 
+/*
+ * Helper function to reset detection ignore
+ */
+
+static void wbsd_reset_ignore(unsigned long data)
+{
+       struct wbsd_host *host = (struct wbsd_host *)data;
+
+       BUG_ON(host == NULL);
+
+       DBG("Resetting card detection ignore\n");
+
+       spin_lock_bh(&host->lock);
+
+       host->flags &= ~WBSD_FIGNORE_DETECT;
+
+       /*
+        * Card status might have changed during the
+        * blackout.
+        */
+       tasklet_schedule(&host->card_tasklet);
+
+       spin_unlock_bh(&host->lock);
+}
+
 /*
  * Tasklets
  */
 
-inline static struct mmc_data* wbsd_get_data(struct wbsd_host* host)
+static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
 {
        WARN_ON(!host->mrq);
        if (!host->mrq)
@@ -1067,72 +1075,69 @@ inline static struct mmc_data* wbsd_get_data(struct wbsd_host* host)
        WARN_ON(!host->mrq->cmd->data);
        if (!host->mrq->cmd->data)
                return NULL;
-       
+
        return host->mrq->cmd->data;
 }
 
 static void wbsd_tasklet_card(unsigned long param)
 {
-       struct wbsd_host* host = (struct wbsd_host*)param;
+       struct wbsd_host *host = (struct wbsd_host *)param;
        u8 csr;
-       int change = 0;
-       
+       int delay = -1;
+
        spin_lock(&host->lock);
-       
-       if (host->flags & WBSD_FIGNORE_DETECT)
-       {
+
+       if (host->flags & WBSD_FIGNORE_DETECT) {
                spin_unlock(&host->lock);
                return;
        }
-       
+
        csr = inb(host->base + WBSD_CSR);
        WARN_ON(csr == 0xff);
-       
-       if (csr & WBSD_CARDPRESENT)
-       {
-               if (!(host->flags & WBSD_FCARD_PRESENT))
-               {
+
+       if (csr & WBSD_CARDPRESENT) {
+               if (!(host->flags & WBSD_FCARD_PRESENT)) {
                        DBG("Card inserted\n");
                        host->flags |= WBSD_FCARD_PRESENT;
-                       change = 1;
+
+                       delay = 500;
                }
-       }
-       else if (host->flags & WBSD_FCARD_PRESENT)
-       {
+       } else if (host->flags & WBSD_FCARD_PRESENT) {
                DBG("Card removed\n");
                host->flags &= ~WBSD_FCARD_PRESENT;
-               change = 1;
-               
-               if (host->mrq)
-               {
-                       printk(KERN_ERR DRIVER_NAME
-                               ": Card removed during transfer!\n");
+
+               if (host->mrq) {
+                       printk(KERN_ERR "%s: Card removed during transfer!\n",
+                               mmc_hostname(host->mmc));
                        wbsd_reset(host);
-                       
+
                        host->mrq->cmd->error = MMC_ERR_FAILED;
                        tasklet_schedule(&host->finish_tasklet);
                }
+
+               delay = 0;
        }
-       
+
        /*
         * Unlock first since we might get a call back.
         */
+
        spin_unlock(&host->lock);
 
-       if (change)
-               mmc_detect_change(host->mmc);
+       if (delay != -1)
+               mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
 }
 
 static void wbsd_tasklet_fifo(unsigned long param)
 {
-       struct wbsd_host* host = (struct wbsd_host*)param;
-       struct mmc_datadata;
-       
+       struct wbsd_host *host = (struct wbsd_host *)param;
+       struct mmc_data *data;
+
        spin_lock(&host->lock);
-               
+
        if (!host->mrq)
                goto end;
-       
+
        data = wbsd_get_data(host);
        if (!data)
                goto end;
@@ -1145,107 +1150,105 @@ static void wbsd_tasklet_fifo(unsigned long param)
        /*
         * Done?
         */
-       if (host->size == data->bytes_xfered)
-       {
+       if (host->size == data->bytes_xfered) {
                wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
                tasklet_schedule(&host->finish_tasklet);
        }
 
-end:   
+end:
        spin_unlock(&host->lock);
 }
 
 static void wbsd_tasklet_crc(unsigned long param)
 {
-       struct wbsd_host* host = (struct wbsd_host*)param;
-       struct mmc_datadata;
-       
+       struct wbsd_host *host = (struct wbsd_host *)param;
+       struct mmc_data *data;
+
        spin_lock(&host->lock);
-       
+
        if (!host->mrq)
                goto end;
-       
+
        data = wbsd_get_data(host);
        if (!data)
                goto end;
-       
+
        DBGF("CRC error\n");
 
        data->error = MMC_ERR_BADCRC;
-       
+
        tasklet_schedule(&host->finish_tasklet);
 
-end:           
+end:
        spin_unlock(&host->lock);
 }
 
 static void wbsd_tasklet_timeout(unsigned long param)
 {
-       struct wbsd_host* host = (struct wbsd_host*)param;
-       struct mmc_datadata;
-       
+       struct wbsd_host *host = (struct wbsd_host *)param;
+       struct mmc_data *data;
+
        spin_lock(&host->lock);
-       
+
        if (!host->mrq)
                goto end;
-       
+
        data = wbsd_get_data(host);
        if (!data)
                goto end;
-       
+
        DBGF("Timeout\n");
 
        data->error = MMC_ERR_TIMEOUT;
-       
+
        tasklet_schedule(&host->finish_tasklet);
 
-end:   
+end:
        spin_unlock(&host->lock);
 }
 
 static void wbsd_tasklet_finish(unsigned long param)
 {
-       struct wbsd_host* host = (struct wbsd_host*)param;
-       struct mmc_datadata;
-       
+       struct wbsd_host *host = (struct wbsd_host *)param;
+       struct mmc_data *data;
+
        spin_lock(&host->lock);
-       
+
        WARN_ON(!host->mrq);
        if (!host->mrq)
                goto end;
-       
+
        data = wbsd_get_data(host);
        if (!data)
                goto end;
 
        wbsd_finish_data(host, data);
-       
-end:   
+
+end:
        spin_unlock(&host->lock);
 }
 
 static void wbsd_tasklet_block(unsigned long param)
 {
-       struct wbsd_host* host = (struct wbsd_host*)param;
-       struct mmc_datadata;
-       
+       struct wbsd_host *host = (struct wbsd_host *)param;
+       struct mmc_data *data;
+
        spin_lock(&host->lock);
 
        if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
-               WBSD_CRC_OK)
-       {
+               WBSD_CRC_OK) {
                data = wbsd_get_data(host);
                if (!data)
                        goto end;
-               
+
                DBGF("CRC error\n");
 
                data->error = MMC_ERR_BADCRC;
-       
+
                tasklet_schedule(&host->finish_tasklet);
        }
 
-end:   
+end:
        spin_unlock(&host->lock);
 }
 
@@ -1255,9 +1258,9 @@ end:
 
 static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
-       struct wbsd_hosthost = dev_id;
+       struct wbsd_host *host = dev_id;
        int isr;
-       
+
        isr = inb(host->base + WBSD_ISR);
 
        /*
@@ -1265,7 +1268,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
         */
        if (isr == 0xff || isr == 0x00)
                return IRQ_NONE;
-       
+
        host->isr |= isr;
 
        /*
@@ -1283,7 +1286,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
                tasklet_hi_schedule(&host->block_tasklet);
        if (isr & WBSD_INT_TC)
                tasklet_schedule(&host->finish_tasklet);
-       
+
        return IRQ_HANDLED;
 }
 
@@ -1297,18 +1300,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
  * Allocate/free MMC structure.
  */
 
-static int __devinit wbsd_alloc_mmc(struct devicedev)
+static int __devinit wbsd_alloc_mmc(struct device *dev)
 {
-       struct mmc_hostmmc;
-       struct wbsd_hosthost;
-       
+       struct mmc_host *mmc;
+       struct wbsd_host *host;
+
        /*
         * Allocate MMC structure.
         */
        mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
        if (!mmc)
                return -ENOMEM;
-       
+
        host = mmc_priv(mmc);
        host->mmc = mmc;
 
@@ -1320,44 +1323,58 @@ static int __devinit wbsd_alloc_mmc(struct device* dev)
        mmc->ops = &wbsd_ops;
        mmc->f_min = 375000;
        mmc->f_max = 24000000;
-       mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
-       
+       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mmc->caps = MMC_CAP_4_BIT_DATA;
+
        spin_lock_init(&host->lock);
-       
+
+       /*
+        * Set up timers
+        */
+       init_timer(&host->ignore_timer);
+       host->ignore_timer.data = (unsigned long)host;
+       host->ignore_timer.function = wbsd_reset_ignore;
+
        /*
         * Maximum number of segments. Worst case is one sector per segment
         * so this will be 64kB/512.
         */
        mmc->max_hw_segs = 128;
        mmc->max_phys_segs = 128;
-       
+
        /*
         * Maximum number of sectors in one transfer. Also limited by 64kB
         * buffer.
         */
        mmc->max_sectors = 128;
-       
+
        /*
         * Maximum segment size. Could be one segment with the maximum number
         * of segments.
         */
        mmc->max_seg_size = mmc->max_sectors * 512;
-       
+
        dev_set_drvdata(dev, mmc);
-       
+
        return 0;
 }
 
-static void __devexit wbsd_free_mmc(struct devicedev)
+static void __devexit wbsd_free_mmc(struct device *dev)
 {
-       struct mmc_host* mmc;
-       
+       struct mmc_host *mmc;
+       struct wbsd_host *host;
+
        mmc = dev_get_drvdata(dev);
        if (!mmc)
                return;
-       
+
+       host = mmc_priv(mmc);
+       BUG_ON(host == NULL);
+
+       del_timer_sync(&host->ignore_timer);
+
        mmc_free_host(mmc);
-       
+
        dev_set_drvdata(dev, NULL);
 }
 
@@ -1365,57 +1382,55 @@ static void __devexit wbsd_free_mmc(struct device* dev)
  * Scan for known chip id:s
  */
 
-static int __devinit wbsd_scan(struct wbsd_hosthost)
+static int __devinit wbsd_scan(struct wbsd_host *host)
 {
        int i, j, k;
        int id;
-       
+
        /*
         * Iterate through all ports, all codes to
         * find hardware that is in our known list.
         */
-       for (i = 0;i < sizeof(config_ports)/sizeof(int);i++)
-       {
+       for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
                if (!request_region(config_ports[i], 2, DRIVER_NAME))
                        continue;
-                       
-               for (j = 0;j < sizeof(unlock_codes)/sizeof(int);j++)
-               {
+
+               for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
                        id = 0xFFFF;
-                       
-                       outb(unlock_codes[j], config_ports[i]);
-                       outb(unlock_codes[j], config_ports[i]);
-                       
+
+                       host->config = config_ports[i];
+                       host->unlock_code = unlock_codes[j];
+
+                       wbsd_unlock_config(host);
+
                        outb(WBSD_CONF_ID_HI, config_ports[i]);
                        id = inb(config_ports[i] + 1) << 8;
 
                        outb(WBSD_CONF_ID_LO, config_ports[i]);
                        id |= inb(config_ports[i] + 1);
-                       
-                       for (k = 0;k < sizeof(valid_ids)/sizeof(int);k++)
-                       {
-                               if (id == valid_ids[k])
-                               {                               
+
+                       wbsd_lock_config(host);
+
+                       for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
+                               if (id == valid_ids[k]) {
                                        host->chip_id = id;
-                                       host->config = config_ports[i];
-                                       host->unlock_code = unlock_codes[i];
-                               
+
                                        return 0;
                                }
                        }
-                       
-                       if (id != 0xFFFF)
-                       {
+
+                       if (id != 0xFFFF) {
                                DBG("Unknown hardware (id %x) found at %x\n",
                                        id, config_ports[i]);
                        }
-
-                       outb(LOCK_CODE, config_ports[i]);
                }
-               
+
                release_region(config_ports[i], 2);
        }
-       
+
+       host->config = 0;
+       host->unlock_code = 0;
+
        return -ENODEV;
 }
 
@@ -1423,29 +1438,29 @@ static int __devinit wbsd_scan(struct wbsd_host* host)
  * Allocate/free io port ranges
  */
 
-static int __devinit wbsd_request_region(struct wbsd_hosthost, int base)
+static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
 {
        if (io & 0x7)
                return -EINVAL;
-       
+
        if (!request_region(base, 8, DRIVER_NAME))
                return -EIO;
-       
+
        host->base = io;
-               
+
        return 0;
 }
 
-static void __devexit wbsd_release_regions(struct wbsd_hosthost)
+static void __devexit wbsd_release_regions(struct wbsd_host *host)
 {
        if (host->base)
                release_region(host->base, 8);
-       
+
        host->base = 0;
 
        if (host->config)
                release_region(host->config, 2);
-       
+
        host->config = 0;
 }
 
@@ -1453,14 +1468,14 @@ static void __devexit wbsd_release_regions(struct wbsd_host* host)
  * Allocate/free DMA port and buffer
  */
 
-static void __devinit wbsd_request_dma(struct wbsd_hosthost, int dma)
+static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
 {
        if (dma < 0)
                return;
-       
+
        if (request_dma(dma, DRIVER_NAME))
                goto err;
-       
+
        /*
         * We need to allocate a special buffer in
         * order for ISA to be able to DMA to it.
@@ -1475,7 +1490,7 @@ static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
         */
        host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer,
                WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
-                       
+
        /*
         * ISA DMA must be aligned on a 64k basis.
         */
@@ -1488,19 +1503,19 @@ static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
                goto kfree;
 
        host->dma = dma;
-       
+
        return;
-       
+
 kfree:
        /*
         * If we've gotten here then there is some kind of alignment bug
         */
        BUG_ON(1);
-       
-       dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
-               DMA_BIDIRECTIONAL);
+
+       dma_unmap_single(host->mmc->dev, host->dma_addr,
+               WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
        host->dma_addr = (dma_addr_t)NULL;
-       
+
        kfree(host->dma_buffer);
        host->dma_buffer = NULL;
 
@@ -1512,16 +1527,16 @@ err:
                "Falling back on FIFO.\n", dma);
 }
 
-static void __devexit wbsd_release_dma(struct wbsd_hosthost)
+static void __devexit wbsd_release_dma(struct wbsd_host *host)
 {
-       if (host->dma_addr)
-               dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
-                       DMA_BIDIRECTIONAL);
-       if (host->dma_buffer)
-               kfree(host->dma_buffer);
+       if (host->dma_addr) {
+               dma_unmap_single(host->mmc->dev, host->dma_addr,
+                       WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+       }
+       kfree(host->dma_buffer);
        if (host->dma >= 0)
                free_dma(host->dma);
-       
+
        host->dma = -1;
        host->dma_buffer = NULL;
        host->dma_addr = (dma_addr_t)NULL;
@@ -1531,10 +1546,10 @@ static void __devexit wbsd_release_dma(struct wbsd_host* host)
  * Allocate/free IRQ.
  */
 
-static int __devinit wbsd_request_irq(struct wbsd_hosthost, int irq)
+static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
 {
        int ret;
-       
+
        /*
         * Allocate interrupt.
         */
@@ -1542,31 +1557,37 @@ static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq)
        ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
        if (ret)
                return ret;
-       
+
        host->irq = irq;
 
        /*
         * Set up tasklets.
         */
-       tasklet_init(&host->card_tasklet, wbsd_tasklet_card, (unsigned long)host);
-       tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, (unsigned long)host);
-       tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, (unsigned long)host);
-       tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, (unsigned long)host);
-       tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host);
-       tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
-       
+       tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
+                       (unsigned long)host);
+       tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
+                       (unsigned long)host);
+       tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
+                       (unsigned long)host);
+       tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
+                       (unsigned long)host);
+       tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
+                       (unsigned long)host);
+       tasklet_init(&host->block_tasklet, wbsd_tasklet_block,
+                       (unsigned long)host);
+
        return 0;
 }
 
-static void __devexit wbsd_release_irq(struct wbsd_hosthost)
+static void __devexit wbsd_release_irq(struct wbsd_host *host)
 {
        if (!host->irq)
                return;
 
        free_irq(host->irq, host);
-       
+
        host->irq = 0;
-               
+
        tasklet_kill(&host->card_tasklet);
        tasklet_kill(&host->fifo_tasklet);
        tasklet_kill(&host->crc_tasklet);
@@ -1579,11 +1600,11 @@ static void __devexit wbsd_release_irq(struct wbsd_host* host)
  * Allocate all resources for the host.
  */
 
-static int __devinit wbsd_request_resources(struct wbsd_hosthost,
+static int __devinit wbsd_request_resources(struct wbsd_host *host,
        int base, int irq, int dma)
 {
        int ret;
-       
+
        /*
         * Allocate I/O ports.
         */
@@ -1602,7 +1623,7 @@ static int __devinit wbsd_request_resources(struct wbsd_host* host,
         * Allocate DMA.
         */
        wbsd_request_dma(host, dma);
-       
+
        return 0;
 }
 
@@ -1610,7 +1631,7 @@ static int __devinit wbsd_request_resources(struct wbsd_host* host,
  * Release all resources for the host.
  */
 
-static void __devexit wbsd_release_resources(struct wbsd_hosthost)
+static void __devexit wbsd_release_resources(struct wbsd_host *host)
 {
        wbsd_release_dma(host);
        wbsd_release_irq(host);
@@ -1621,11 +1642,13 @@ static void __devexit wbsd_release_resources(struct wbsd_host* host)
  * Configure the resources the chip should use.
  */
 
-static void __devinit wbsd_chip_config(struct wbsd_host* host)
+static void wbsd_chip_config(struct wbsd_host *host)
 {
+       wbsd_unlock_config(host);
+
        /*
         * Reset the chip.
-        */     
+        */
        wbsd_write_config(host, WBSD_CONF_SWRST, 1);
        wbsd_write_config(host, WBSD_CONF_SWRST, 0);
 
@@ -1633,53 +1656,59 @@ static void __devinit wbsd_chip_config(struct wbsd_host* host)
         * Select SD/MMC function.
         */
        wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
-       
+
        /*
         * Set up card detection.
         */
        wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
-       
+
        /*
         * Configure chip
         */
        wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
        wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
-       
+
        wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
-       
+
        if (host->dma >= 0)
                wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
-       
+
        /*
         * Enable and power up chip.
         */
        wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
        wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+
+       wbsd_lock_config(host);
 }
 
 /*
  * Check that configured resources are correct.
  */
-static int __devinit wbsd_chip_validate(struct wbsd_host* host)
+
+static int wbsd_chip_validate(struct wbsd_host *host)
 {
        int base, irq, dma;
-       
+
+       wbsd_unlock_config(host);
+
        /*
         * Select SD/MMC function.
         */
        wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
-       
+
        /*
         * Read configuration.
         */
        base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
        base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
-       
+
        irq = wbsd_read_config(host, WBSD_CONF_IRQ);
-       
+
        dma = wbsd_read_config(host, WBSD_CONF_DRQ);
-       
+
+       wbsd_lock_config(host);
+
        /*
         * Validate against given configuration.
         */
@@ -1689,84 +1718,93 @@ static int __devinit wbsd_chip_validate(struct wbsd_host* host)
                return 0;
        if ((dma != host->dma) && (host->dma != -1))
                return 0;
-       
+
        return 1;
 }
 
+/*
+ * Powers down the SD function
+ */
+
+static void wbsd_chip_poweroff(struct wbsd_host *host)
+{
+       wbsd_unlock_config(host);
+
+       wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+       wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
+
+       wbsd_lock_config(host);
+}
+
 /*****************************************************************************\
  *                                                                           *
  * Devices setup and shutdown                                                *
  *                                                                           *
 \*****************************************************************************/
 
-static int __devinit wbsd_init(struct devicedev, int base, int irq, int dma,
+static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
        int pnp)
 {
-       struct wbsd_hosthost = NULL;
-       struct mmc_hostmmc = NULL;
+       struct wbsd_host *host = NULL;
+       struct mmc_host *mmc = NULL;
        int ret;
-       
+
        ret = wbsd_alloc_mmc(dev);
        if (ret)
                return ret;
-       
+
        mmc = dev_get_drvdata(dev);
        host = mmc_priv(mmc);
-       
+
        /*
         * Scan for hardware.
         */
        ret = wbsd_scan(host);
-       if (ret)
-       {
-               if (pnp && (ret == -ENODEV))
-               {
+       if (ret) {
+               if (pnp && (ret == -ENODEV)) {
                        printk(KERN_WARNING DRIVER_NAME
                                ": Unable to confirm device presence. You may "
                                "experience lock-ups.\n");
-               }
-               else
-               {
+               } else {
                        wbsd_free_mmc(dev);
                        return ret;
                }
        }
-       
+
        /*
         * Request resources.
         */
        ret = wbsd_request_resources(host, io, irq, dma);
-       if (ret)
-       {
+       if (ret) {
                wbsd_release_resources(host);
                wbsd_free_mmc(dev);
                return ret;
        }
-       
+
        /*
         * See if chip needs to be configured.
         */
-       if (pnp && (host->config != 0))
-       {
-               if (!wbsd_chip_validate(host))
-               {
+       if (pnp) {
+               if ((host->config != 0) && !wbsd_chip_validate(host)) {
                        printk(KERN_WARNING DRIVER_NAME
                                ": PnP active but chip not configured! "
                                "You probably have a buggy BIOS. "
                                "Configuring chip manually.\n");
                        wbsd_chip_config(host);
                }
-       }
-       else
+       } else
                wbsd_chip_config(host);
-       
+
        /*
         * Power Management stuff. No idea how this works.
         * Not tested.
         */
 #ifdef CONFIG_PM
-       if (host->config)
+       if (host->config) {
+               wbsd_unlock_config(host);
                wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+               wbsd_lock_config(host);
+       }
 #endif
        /*
         * Allow device to initialise itself properly.
@@ -1777,10 +1815,10 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma,
         * Reset the chip into a known state.
         */
        wbsd_init_device(host);
-       
+
        mmc_add_host(mmc);
 
-       printk(KERN_INFO "%s: W83L51xD", mmc->host_name);
+       printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
        if (host->chip_id != 0)
                printk(" id %x", (int)host->chip_id);
        printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
@@ -1795,31 +1833,26 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma,
        return 0;
 }
 
-static void __devexit wbsd_shutdown(struct devicedev, int pnp)
+static void __devexit wbsd_shutdown(struct device *dev, int pnp)
 {
-       struct mmc_hostmmc = dev_get_drvdata(dev);
-       struct wbsd_hosthost;
-       
+       struct mmc_host *mmc = dev_get_drvdata(dev);
+       struct wbsd_host *host;
+
        if (!mmc)
                return;
 
        host = mmc_priv(mmc);
-       
+
        mmc_remove_host(mmc);
 
+       /*
+        * Power down the SD/MMC function.
+        */
        if (!pnp)
-       {
-               /*
-                * Power down the SD/MMC function.
-                */
-               wbsd_unlock_config(host);
-               wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
-               wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
-               wbsd_lock_config(host);
-       }
-       
+               wbsd_chip_poweroff(host);
+
        wbsd_release_resources(host);
-       
+
        wbsd_free_mmc(dev);
 }
 
@@ -1827,14 +1860,14 @@ static void __devexit wbsd_shutdown(struct device* dev, int pnp)
  * Non-PnP
  */
 
-static int __devinit wbsd_probe(struct device* dev)
+static int __devinit wbsd_probe(struct platform_device *dev)
 {
-       return wbsd_init(dev, io, irq, dma, 0);
+       return wbsd_init(&dev->dev, io, irq, dma, 0);
 }
 
-static int __devexit wbsd_remove(struct device* dev)
+static int __devexit wbsd_remove(struct platform_device *dev)
 {
-       wbsd_shutdown(dev, 0);
+       wbsd_shutdown(&dev->dev, 0);
 
        return 0;
 }
@@ -1846,10 +1879,10 @@ static int __devexit wbsd_remove(struct device* dev)
 #ifdef CONFIG_PNP
 
 static int __devinit
-wbsd_pnp_probe(struct pnp_dev * pnpdev, const struct pnp_device_id *dev_id)
+wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
 {
        int io, irq, dma;
-       
+
        /*
         * Get resources from PnP layer.
         */
@@ -1859,13 +1892,13 @@ wbsd_pnp_probe(struct pnp_dev * pnpdev, const struct pnp_device_id *dev_id)
                dma = pnp_dma(pnpdev, 0);
        else
                dma = -1;
-       
+
        DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
-       
+
        return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
 }
 
-static void __devexit wbsd_pnp_remove(struct pnp_dev * dev)
+static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
 {
        wbsd_shutdown(&dev->dev, 1);
 }
@@ -1877,34 +1910,141 @@ static void __devexit wbsd_pnp_remove(struct pnp_dev * dev)
  */
 
 #ifdef CONFIG_PM
-static int wbsd_suspend(struct device *dev, pm_message_t state, u32 level)
+
+static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
 {
-       DBGF("Not yet supported\n");
+       BUG_ON(host == NULL);
 
-       return 0;
+       return mmc_suspend_host(host->mmc, state);
 }
 
-static int wbsd_resume(struct device *dev, u32 level)
+static int wbsd_resume(struct wbsd_host *host)
 {
-       DBGF("Not yet supported\n");
+       BUG_ON(host == NULL);
+
+       wbsd_init_device(host);
+
+       return mmc_resume_host(host->mmc);
+}
+
+static int wbsd_platform_suspend(struct platform_device *dev,
+                                pm_message_t state)
+{
+       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct wbsd_host *host;
+       int ret;
+
+       if (mmc == NULL)
+               return 0;
+
+       DBGF("Suspending...\n");
+
+       host = mmc_priv(mmc);
+
+       ret = wbsd_suspend(host, state);
+       if (ret)
+               return ret;
+
+       wbsd_chip_poweroff(host);
 
        return 0;
 }
-#else
-#define wbsd_suspend NULL
-#define wbsd_resume NULL
-#endif
+
+static int wbsd_platform_resume(struct platform_device *dev)
+{
+       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct wbsd_host *host;
+
+       if (mmc == NULL)
+               return 0;
+
+       DBGF("Resuming...\n");
+
+       host = mmc_priv(mmc);
+
+       wbsd_chip_config(host);
+
+       /*
+        * Allow device to initialise itself properly.
+        */
+       mdelay(5);
+
+       return wbsd_resume(host);
+}
+
+#ifdef CONFIG_PNP
+
+static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+       struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
+       struct wbsd_host *host;
+
+       if (mmc == NULL)
+               return 0;
+
+       DBGF("Suspending...\n");
+
+       host = mmc_priv(mmc);
+
+       return wbsd_suspend(host, state);
+}
+
+static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
+{
+       struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
+       struct wbsd_host *host;
+
+       if (mmc == NULL)
+               return 0;
+
+       DBGF("Resuming...\n");
+
+       host = mmc_priv(mmc);
+
+       /*
+        * See if chip needs to be configured.
+        */
+       if (host->config != 0) {
+               if (!wbsd_chip_validate(host)) {
+                       printk(KERN_WARNING DRIVER_NAME
+                               ": PnP active but chip not configured! "
+                               "You probably have a buggy BIOS. "
+                               "Configuring chip manually.\n");
+                       wbsd_chip_config(host);
+               }
+       }
+
+       /*
+        * Allow device to initialise itself properly.
+        */
+       mdelay(5);
+
+       return wbsd_resume(host);
+}
+
+#endif /* CONFIG_PNP */
+
+#else /* CONFIG_PM */
+
+#define wbsd_platform_suspend NULL
+#define wbsd_platform_resume NULL
+
+#define wbsd_pnp_suspend NULL
+#define wbsd_pnp_resume NULL
+
+#endif /* CONFIG_PM */
 
 static struct platform_device *wbsd_device;
 
-static struct device_driver wbsd_driver = {
-       .name           = DRIVER_NAME,
-       .bus            = &platform_bus_type,
+static struct platform_driver wbsd_driver = {
        .probe          = wbsd_probe,
-       .remove         = wbsd_remove,
-       
-       .suspend        = wbsd_suspend,
-       .resume         = wbsd_resume,
+       .remove         = __devexit_p(wbsd_remove),
+
+       .suspend        = wbsd_platform_suspend,
+       .resume         = wbsd_platform_resume,
+       .driver         = {
+               .name   = DRIVER_NAME,
+       },
 };
 
 #ifdef CONFIG_PNP
@@ -1913,7 +2053,10 @@ static struct pnp_driver wbsd_pnp_driver = {
        .name           = DRIVER_NAME,
        .id_table       = pnp_dev_table,
        .probe          = wbsd_pnp_probe,
-       .remove         = wbsd_pnp_remove,
+       .remove         = __devexit_p(wbsd_pnp_remove),
+
+       .suspend        = wbsd_pnp_suspend,
+       .resume         = wbsd_pnp_resume,
 };
 
 #endif /* CONFIG_PNP */
@@ -1925,7 +2068,7 @@ static struct pnp_driver wbsd_pnp_driver = {
 static int __init wbsd_drv_init(void)
 {
        int result;
-       
+
        printk(KERN_INFO DRIVER_NAME
                ": Winbond W83L51xD SD/MMC card interface driver, "
                DRIVER_VERSION "\n");
@@ -1933,25 +2076,30 @@ static int __init wbsd_drv_init(void)
 
 #ifdef CONFIG_PNP
 
-       if (!nopnp)
-       {
+       if (!nopnp) {
                result = pnp_register_driver(&wbsd_pnp_driver);
                if (result < 0)
                        return result;
        }
+#endif /* CONFIG_PNP */
 
-#endif /* CONFIG_PNP */        
-       
-       if (nopnp)
-       {
-               result = driver_register(&wbsd_driver);
+       if (nopnp) {
+               result = platform_driver_register(&wbsd_driver);
                if (result < 0)
                        return result;
 
-               wbsd_device = platform_device_register_simple(DRIVER_NAME, -1,
-                       NULL, 0);
-               if (IS_ERR(wbsd_device))
-                       return PTR_ERR(wbsd_device);
+               wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
+               if (!wbsd_device) {
+                       platform_driver_unregister(&wbsd_driver);
+                       return -ENOMEM;
+               }
+
+               result = platform_device_add(wbsd_device);
+               if (result) {
+                       platform_device_put(wbsd_device);
+                       platform_driver_unregister(&wbsd_driver);
+                       return result;
+               }
        }
 
        return 0;
@@ -1963,14 +2111,13 @@ static void __exit wbsd_drv_exit(void)
 
        if (!nopnp)
                pnp_unregister_driver(&wbsd_pnp_driver);
-       
-#endif /* CONFIG_PNP */        
 
-       if (nopnp)
-       {
+#endif /* CONFIG_PNP */
+
+       if (nopnp) {
                platform_device_unregister(wbsd_device);
-       
-               driver_unregister(&wbsd_driver);
+
+               platform_driver_unregister(&wbsd_driver);
        }
 
        DBG("unloaded\n");
@@ -1986,6 +2133,7 @@ module_param(irq, uint, 0444);
 module_param(dma, int, 0444);
 
 MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
 MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
 MODULE_VERSION(DRIVER_VERSION);