memstick: automatically retrieve "INT" value from command response
[sfrench/cifs-2.6.git] / drivers / memstick / host / tifm_ms.c
index 4fb24215bd952aba5a8ab20dc59cd22bee24f87c..eb150dfb637f150da1765e096edda10661312dc1 100644 (file)
 #include <asm/io.h>
 
 #define DRIVER_NAME "tifm_ms"
-#define DRIVER_VERSION "0.1"
 
 static int no_dma;
 module_param(no_dma, bool, 0644);
 
-#define TIFM_MS_TIMEOUT      0x00100
-#define TIFM_MS_BADCRC       0x00200
-#define TIFM_MS_EOTPC        0x01000
-#define TIFM_MS_INT          0x02000
-
-/* The meaning of the bit majority in this constant is unknown. */
-#define TIFM_MS_SERIAL       0x04010
+/*
+ * Some control bits of TIFM appear to conform to Sony's reference design,
+ * so I'm just assuming they all are.
+ */
 
-#define TIFM_MS_SYS_LATCH    0x00100
-#define TIFM_MS_SYS_NOT_RDY  0x00800
-#define TIFM_MS_SYS_DATA     0x10000
+#define TIFM_MS_STAT_DRQ     0x04000
+#define TIFM_MS_STAT_MSINT   0x02000
+#define TIFM_MS_STAT_RDY     0x01000
+#define TIFM_MS_STAT_CRC     0x00200
+#define TIFM_MS_STAT_TOE     0x00100
+#define TIFM_MS_STAT_EMP     0x00020
+#define TIFM_MS_STAT_FUL     0x00010
+#define TIFM_MS_STAT_CED     0x00008
+#define TIFM_MS_STAT_ERR     0x00004
+#define TIFM_MS_STAT_BRQ     0x00002
+#define TIFM_MS_STAT_CNK     0x00001
+
+#define TIFM_MS_SYS_DMA      0x10000
+#define TIFM_MS_SYS_RESET    0x08000
+#define TIFM_MS_SYS_SRAC     0x04000
+#define TIFM_MS_SYS_INTEN    0x02000
+#define TIFM_MS_SYS_NOCRC    0x01000
+#define TIFM_MS_SYS_INTCLR   0x00800
+#define TIFM_MS_SYS_MSIEN    0x00400
+#define TIFM_MS_SYS_FCLR     0x00200
+#define TIFM_MS_SYS_FDIR     0x00100
+#define TIFM_MS_SYS_DAM      0x00080
+#define TIFM_MS_SYS_DRM      0x00040
+#define TIFM_MS_SYS_DRQSL    0x00020
+#define TIFM_MS_SYS_REI      0x00010
+#define TIFM_MS_SYS_REO      0x00008
+#define TIFM_MS_SYS_BSY_MASK 0x00007
+
+#define TIFM_MS_SYS_FIFO     (TIFM_MS_SYS_INTEN | TIFM_MS_SYS_MSIEN \
+                             | TIFM_MS_SYS_FCLR | TIFM_MS_SYS_BSY_MASK)
 
 /* Hardware flags */
 enum {
-       CMD_READY  = 0x0001,
-       FIFO_READY = 0x0002,
-       CARD_READY = 0x0004,
-       DATA_CARRY = 0x0008
+       CMD_READY  = 0x01,
+       FIFO_READY = 0x02,
+       CARD_INT   = 0x04
 };
 
 struct tifm_ms {
        struct tifm_dev         *dev;
-       unsigned short          eject:1,
-                               no_dma:1;
-       unsigned short          cmd_flags;
+       struct timer_list       timer;
+       struct memstick_request *req;
        unsigned int            mode_mask;
        unsigned int            block_pos;
        unsigned long           timeout_jiffies;
-
-       struct timer_list       timer;
-       struct memstick_request *req;
+       unsigned char           eject:1,
+                               use_dma:1;
+       unsigned char           cmd_flags;
+       unsigned char           io_pos;
        unsigned int            io_word;
 };
 
-static void tifm_ms_read_fifo(struct tifm_ms *host, unsigned int fifo_offset,
-                             struct page *pg, unsigned int page_off,
-                             unsigned int length)
+static unsigned int tifm_ms_read_data(struct tifm_ms *host,
+                                     unsigned char *buf, unsigned int length)
 {
        struct tifm_dev *sock = host->dev;
-       unsigned int cnt = 0, off = 0;
-       unsigned char *buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + page_off;
+       unsigned int off = 0;
 
-       if (host->cmd_flags & DATA_CARRY) {
-               while ((fifo_offset & 3) && length) {
+       while (host->io_pos && length) {
+               buf[off++] = host->io_word & 0xff;
+               host->io_word >>= 8;
+               length--;
+               host->io_pos--;
+       }
+
+       if (!length)
+               return off;
+
+       while (!(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) {
+               if (length < 4)
+                       break;
+               *(unsigned int *)(buf + off) = __raw_readl(sock->addr
+                                                          + SOCK_MS_DATA);
+               length -= 4;
+               off += 4;
+       }
+
+       if (length
+           && !(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) {
+               host->io_word = readl(sock->addr + SOCK_MS_DATA);
+               for (host->io_pos = 4; host->io_pos; --host->io_pos) {
                        buf[off++] = host->io_word & 0xff;
                        host->io_word >>= 8;
                        length--;
-                       fifo_offset++;
+                       if (!length)
+                               break;
                }
-               if (!(fifo_offset & 3))
-                       host->cmd_flags &= ~DATA_CARRY;
-               if (!length)
-                       return;
        }
 
-       do {
-               host->io_word = readl(sock->addr + SOCK_FIFO_ACCESS
-                                     + fifo_offset);
-               cnt = 4;
-               while (length && cnt) {
-                       buf[off++] = (host->io_word >> 8) & 0xff;
-                       cnt--;
-                       length--;
-               }
-               fifo_offset += 4 - cnt;
-       } while (length);
-
-       if (cnt)
-               host->cmd_flags |= DATA_CARRY;
-
-       kunmap_atomic(buf - page_off, KM_BIO_DST_IRQ);
+       return off;
 }
 
-static void tifm_ms_write_fifo(struct tifm_ms *host, unsigned int fifo_offset,
-                              struct page *pg, unsigned int page_off,
-                              unsigned int length)
+static unsigned int tifm_ms_write_data(struct tifm_ms *host,
+                                      unsigned char *buf, unsigned int length)
 {
        struct tifm_dev *sock = host->dev;
-       unsigned int cnt = 0, off = 0;
-       unsigned char *buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + page_off;
+       unsigned int off = 0;
 
-       if (host->cmd_flags & DATA_CARRY) {
-               while (fifo_offset & 3) {
-                       host->io_word |= buf[off++] << (8 * (fifo_offset & 3));
+       if (host->io_pos) {
+               while (host->io_pos < 4 && length) {
+                       host->io_word |=  buf[off++] << (host->io_pos * 8);
+                       host->io_pos++;
                        length--;
-                       fifo_offset++;
                }
-               if (!(fifo_offset & 3)) {
-                       writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
-                              + fifo_offset - 4);
-
-                       host->cmd_flags &= ~DATA_CARRY;
-               }
-               if (!length)
-                       return;
        }
 
-       do {
-               cnt = 4;
+       if (host->io_pos == 4
+           && !(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) {
+               writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM),
+                      sock->addr + SOCK_MS_SYSTEM);
+               writel(host->io_word, sock->addr + SOCK_MS_DATA);
+               host->io_pos = 0;
                host->io_word = 0;
-               while (length && cnt) {
-                       host->io_word |= buf[off++] << (4 - cnt);
-                       cnt--;
-                       length--;
-               }
-               fifo_offset += 4 - cnt;
-               if (!cnt)
-                       writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
-                                             + fifo_offset - 4);
-
-       } while (length);
-
-       if (cnt)
-               host->cmd_flags |= DATA_CARRY;
+       } else if (host->io_pos) {
+               return off;
+       }
 
-       kunmap_atomic(buf - page_off, KM_BIO_SRC_IRQ);
-}
+       if (!length)
+               return off;
 
-static void tifm_ms_move_block(struct tifm_ms *host, unsigned int length)
-{
-       unsigned int t_size;
-       unsigned int off = host->req->sg.offset + host->block_pos;
-       unsigned int p_off, p_cnt;
-       struct page *pg;
-       unsigned long flags;
+       while (!(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) {
+               if (length < 4)
+                       break;
+               writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM),
+                      sock->addr + SOCK_MS_SYSTEM);
+               __raw_writel(*(unsigned int *)(buf + off),
+                            sock->addr + SOCK_MS_DATA);
+               length -= 4;
+               off += 4;
+       }
 
-       dev_dbg(&host->dev->dev, "moving block\n");
-       local_irq_save(flags);
-       t_size = length;
-       while (t_size) {
-               pg = nth_page(sg_page(&host->req->sg), off >> PAGE_SHIFT);
-               p_off = offset_in_page(off);
-               p_cnt = PAGE_SIZE - p_off;
-               p_cnt = min(p_cnt, t_size);
+       switch (length) {
+       case 3:
+               host->io_word |= buf[off + 2] << 16;
+               host->io_pos++;
+       case 2:
+               host->io_word |= buf[off + 1] << 8;
+               host->io_pos++;
+       case 1:
+               host->io_word |= buf[off];
+               host->io_pos++;
+       }
 
-               if (host->req->data_dir == WRITE)
-                       tifm_ms_write_fifo(host, length - t_size,
-                                          pg, p_off, p_cnt);
-               else
-                       tifm_ms_read_fifo(host, length - t_size,
-                                         pg, p_off, p_cnt);
+       off += host->io_pos;
 
-               t_size -= p_cnt;
-       }
-       local_irq_restore(flags);
+       return off;
 }
 
-static int tifm_ms_transfer_data(struct tifm_ms *host, int skip)
+static unsigned int tifm_ms_transfer_data(struct tifm_ms *host)
 {
        struct tifm_dev *sock = host->dev;
-       unsigned int length = host->req->sg.length - host->block_pos;
+       unsigned int length;
+       unsigned int off;
+       unsigned int t_size, p_off, p_cnt;
+       unsigned char *buf;
+       struct page *pg;
+       unsigned long flags = 0;
+
+       if (host->req->long_data) {
+               length = host->req->sg.length - host->block_pos;
+               off = host->req->sg.offset + host->block_pos;
+       } else {
+               length = host->req->data_len - host->block_pos;
+               off = 0;
+       }
+       dev_dbg(&sock->dev, "fifo data transfer, %d, %d\n", length,
+               host->block_pos);
+
+       while (length) {
+               if (host->req->long_data) {
+                       pg = nth_page(sg_page(&host->req->sg),
+                                     off >> PAGE_SHIFT);
+                       p_off = offset_in_page(off);
+                       p_cnt = PAGE_SIZE - p_off;
+                       p_cnt = min(p_cnt, length);
+
+                       local_irq_save(flags);
+                       buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + p_off;
+               } else {
+                       buf = host->req->data + host->block_pos;
+                       p_cnt = host->req->data_len - host->block_pos;
+               }
 
-       if (!length)
-               return 1;
+               t_size = host->req->data_dir == WRITE
+                        ? tifm_ms_write_data(host, buf, p_cnt)
+                        : tifm_ms_read_data(host, buf, p_cnt);
 
-       if (length > TIFM_FIFO_SIZE)
-               length = TIFM_FIFO_SIZE;
+               if (host->req->long_data) {
+                       kunmap_atomic(buf - p_off, KM_BIO_SRC_IRQ);
+                       local_irq_restore(flags);
+               }
 
-       if (!skip) {
-               tifm_ms_move_block(host, length);
-               host->block_pos += length;
+               if (!t_size)
+                       break;
+               host->block_pos += t_size;
+               length -= t_size;
+               off += t_size;
        }
 
-       if ((host->req->data_dir == READ)
-           && (host->block_pos == host->req->sg.length))
-               return 1;
-
-       writel(ilog2(length) - 2, sock->addr + SOCK_FIFO_PAGE_SIZE);
-       if (host->req->data_dir == WRITE)
-               writel((1 << 8) | TIFM_DMA_TX, sock->addr + SOCK_DMA_CONTROL);
-       else
-               writel((1 << 8), sock->addr + SOCK_DMA_CONTROL);
+       dev_dbg(&sock->dev, "fifo data transfer, %d remaining\n", length);
+       if (!length && (host->req->data_dir == WRITE)) {
+               if (host->io_pos) {
+                       writel(TIFM_MS_SYS_FDIR
+                              | readl(sock->addr + SOCK_MS_SYSTEM),
+                              sock->addr + SOCK_MS_SYSTEM);
+                       writel(host->io_word, sock->addr + SOCK_MS_DATA);
+               }
+               writel(TIFM_MS_SYS_FDIR
+                      | readl(sock->addr + SOCK_MS_SYSTEM),
+                      sock->addr + SOCK_MS_SYSTEM);
+               writel(0, sock->addr + SOCK_MS_DATA);
+       } else {
+               readl(sock->addr + SOCK_MS_DATA);
+       }
 
-       return 0;
+       return length;
 }
 
 static int tifm_ms_issue_cmd(struct tifm_ms *host)
 {
        struct tifm_dev *sock = host->dev;
        unsigned char *data;
-       unsigned int data_len = 0, cmd = 0, cmd_mask = 0, cnt, tval = 0;
+       unsigned int data_len, cmd, sys_param;
 
+       host->cmd_flags = 0;
+       host->block_pos = 0;
+       host->io_pos = 0;
+       host->io_word = 0;
        host->cmd_flags = 0;
 
-       if (host->req->io_type == MEMSTICK_IO_SG) {
-               if (!host->no_dma) {
-                       if (1 != tifm_map_sg(sock, &host->req->sg, 1,
-                                            host->req->data_dir == READ
-                                            ? PCI_DMA_FROMDEVICE
-                                            : PCI_DMA_TODEVICE)) {
-                               host->req->error = -ENOMEM;
-                               return host->req->error;
-                       }
-                       data_len = sg_dma_len(&host->req->sg);
-               } else
-                       data_len = host->req->sg.length;
+       data = host->req->data;
 
-               writel(TIFM_FIFO_INT_SETALL,
-                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
-               writel(TIFM_FIFO_ENABLE,
-                      sock->addr + SOCK_FIFO_CONTROL);
-               writel(TIFM_FIFO_INTMASK,
-                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+       host->use_dma = !no_dma;
 
-               if (!host->no_dma) {
-                       writel(ilog2(data_len) - 2,
-                              sock->addr + SOCK_FIFO_PAGE_SIZE);
-                       writel(sg_dma_address(&host->req->sg),
-                              sock->addr + SOCK_DMA_ADDRESS);
-                       if (host->req->data_dir == WRITE)
-                               writel((1 << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
-                                      sock->addr + SOCK_DMA_CONTROL);
-                       else
-                               writel((1 << 8) | TIFM_DMA_EN,
-                                      sock->addr + SOCK_DMA_CONTROL);
-               } else {
-                       tifm_ms_transfer_data(host,
-                                             host->req->data_dir == READ);
-               }
-
-               cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
-               cmd_mask |= TIFM_MS_SYS_DATA | TIFM_MS_SYS_NOT_RDY;
-               writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
-       } else if (host->req->io_type == MEMSTICK_IO_VAL) {
-               data = host->req->data;
+       if (host->req->long_data) {
+               data_len = host->req->sg.length;
+               if (!is_power_of_2(data_len))
+                       host->use_dma = 0;
+       } else {
                data_len = host->req->data_len;
+               host->use_dma = 0;
+       }
 
-               cmd_mask = host->mode_mask | 0x2607; /* unknown constant */
-
-               if (host->req->data_dir == WRITE) {
-                       cmd_mask |= TIFM_MS_SYS_LATCH;
-                       writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
-                       for (cnt = 0; (data_len - cnt) >= 4; cnt += 4) {
-                               writel(TIFM_MS_SYS_LATCH
-                                      | readl(sock->addr + SOCK_MS_SYSTEM),
-                                      sock->addr + SOCK_MS_SYSTEM);
-                               __raw_writel(*(unsigned int *)(data + cnt),
-                                            sock->addr + SOCK_MS_DATA);
-                               dev_dbg(&sock->dev, "writing %x\n",
-                                       *(int *)(data + cnt));
-                       }
-                       switch (data_len - cnt) {
-                       case 3:
-                               tval |= data[cnt + 2] << 16;
-                       case 2:
-                               tval |= data[cnt + 1] << 8;
-                       case 1:
-                               tval |= data[cnt];
-                               writel(TIFM_MS_SYS_LATCH
-                                      | readl(sock->addr + SOCK_MS_SYSTEM),
-                                      sock->addr + SOCK_MS_SYSTEM);
-                               writel(tval, sock->addr + SOCK_MS_DATA);
-                               dev_dbg(&sock->dev, "writing %x\n", tval);
-                       }
+       writel(TIFM_FIFO_INT_SETALL,
+              sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+       writel(TIFM_FIFO_ENABLE,
+              sock->addr + SOCK_FIFO_CONTROL);
+
+       if (host->use_dma) {
+               if (1 != tifm_map_sg(sock, &host->req->sg, 1,
+                                    host->req->data_dir == READ
+                                    ? PCI_DMA_FROMDEVICE
+                                    : PCI_DMA_TODEVICE)) {
+                       host->req->error = -ENOMEM;
+                       return host->req->error;
+               }
+               data_len = sg_dma_len(&host->req->sg);
 
-                       writel(TIFM_MS_SYS_LATCH
-                              | readl(sock->addr + SOCK_MS_SYSTEM),
-                              sock->addr + SOCK_MS_SYSTEM);
-                       writel(0, sock->addr + SOCK_MS_DATA);
-                       dev_dbg(&sock->dev, "writing %x\n", 0);
+               writel(ilog2(data_len) - 2,
+                      sock->addr + SOCK_FIFO_PAGE_SIZE);
+               writel(TIFM_FIFO_INTMASK,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+               sys_param = TIFM_DMA_EN | (1 << 8);
+               if (host->req->data_dir == WRITE)
+                       sys_param |= TIFM_DMA_TX;
+
+               writel(TIFM_FIFO_INTMASK,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
 
-               } else
-                       writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+               writel(sg_dma_address(&host->req->sg),
+                      sock->addr + SOCK_DMA_ADDRESS);
+               writel(sys_param, sock->addr + SOCK_DMA_CONTROL);
+       } else {
+               writel(host->mode_mask | TIFM_MS_SYS_FIFO,
+                      sock->addr + SOCK_MS_SYSTEM);
 
-               cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
-               cmd_mask &= ~TIFM_MS_SYS_DATA;
-               cmd_mask |= TIFM_MS_SYS_NOT_RDY;
-               dev_dbg(&sock->dev, "mask %x\n", cmd_mask);
-               writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
-       } else
-               BUG();
+               writel(TIFM_FIFO_MORE,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+       }
 
        mod_timer(&host->timer, jiffies + host->timeout_jiffies);
        writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
               sock->addr + SOCK_CONTROL);
        host->req->error = 0;
 
+       sys_param = readl(sock->addr + SOCK_MS_SYSTEM);
+       sys_param |= TIFM_MS_SYS_INTCLR;
+
+       if (host->use_dma)
+               sys_param |= TIFM_MS_SYS_DMA;
+       else
+               sys_param &= ~TIFM_MS_SYS_DMA;
+
+       writel(sys_param, sock->addr + SOCK_MS_SYSTEM);
+
        cmd = (host->req->tpc & 0xf) << 12;
        cmd |= data_len;
        writel(cmd, sock->addr + SOCK_MS_COMMAND);
 
-       dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, cmd_mask);
+       dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, sys_param);
        return 0;
 }
 
@@ -314,47 +336,29 @@ static void tifm_ms_complete_cmd(struct tifm_ms *host)
 {
        struct tifm_dev *sock = host->dev;
        struct memstick_host *msh = tifm_get_drvdata(sock);
-       unsigned int tval = 0, data_len;
-       unsigned char *data;
        int rc;
 
        del_timer(&host->timer);
-       if (host->req->io_type == MEMSTICK_IO_SG) {
-               if (!host->no_dma)
-                       tifm_unmap_sg(sock, &host->req->sg, 1,
-                                     host->req->data_dir == READ
-                                     ? PCI_DMA_FROMDEVICE
-                                     : PCI_DMA_TODEVICE);
-       } else if (host->req->io_type == MEMSTICK_IO_VAL) {
-               writel(~TIFM_MS_SYS_DATA & readl(sock->addr + SOCK_MS_SYSTEM),
-                      sock->addr + SOCK_MS_SYSTEM);
 
-               data = host->req->data;
-               data_len = host->req->data_len;
+       host->req->int_reg = readl(sock->addr + SOCK_MS_STATUS) & 0xff;
+       host->req->int_reg = (host->req->int_reg & 1)
+                            | ((host->req->int_reg << 4) & 0xe0);
 
-               if (host->req->data_dir == READ) {
-                       for (rc = 0; (data_len - rc) >= 4; rc += 4)
-                               *(int *)(data + rc)
-                                       = __raw_readl(sock->addr
-                                                     + SOCK_MS_DATA);
-
-                       if (data_len - rc)
-                               tval = readl(sock->addr + SOCK_MS_DATA);
-                       switch (data_len - rc) {
-                       case 3:
-                               data[rc + 2] = (tval >> 16) & 0xff;
-                       case 2:
-                               data[rc + 1] = (tval >> 8) & 0xff;
-                       case 1:
-                               data[rc] = tval & 0xff;
-                       }
-                       readl(sock->addr + SOCK_MS_DATA);
-               }
+       writel(TIFM_FIFO_INT_SETALL,
+              sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+       writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+
+       if (host->use_dma) {
+               tifm_unmap_sg(sock, &host->req->sg, 1,
+                             host->req->data_dir == READ
+                             ? PCI_DMA_FROMDEVICE
+                             : PCI_DMA_TODEVICE);
        }
 
        writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
               sock->addr + SOCK_CONTROL);
 
+       dev_dbg(&sock->dev, "TPC complete\n");
        do {
                rc = memstick_next_req(msh, &host->req);
        } while (!rc && tifm_ms_issue_cmd(host));
@@ -365,11 +369,10 @@ static int tifm_ms_check_status(struct tifm_ms *host)
        if (!host->req->error) {
                if (!(host->cmd_flags & CMD_READY))
                        return 1;
-               if ((host->req->io_type == MEMSTICK_IO_SG)
-                   && !(host->cmd_flags & FIFO_READY))
+               if (!(host->cmd_flags & FIFO_READY))
                        return 1;
                if (host->req->need_card_int
-                   && !(host->cmd_flags & CARD_READY))
+                   && !(host->cmd_flags & CARD_INT))
                        return 1;
        }
        return 0;
@@ -379,18 +382,24 @@ static int tifm_ms_check_status(struct tifm_ms *host)
 static void tifm_ms_data_event(struct tifm_dev *sock)
 {
        struct tifm_ms *host;
-       unsigned int fifo_status = 0;
+       unsigned int fifo_status = 0, host_status = 0;
        int rc = 1;
 
        spin_lock(&sock->lock);
        host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
        fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
-       dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
-               fifo_status, host->cmd_flags);
+       host_status = readl(sock->addr + SOCK_MS_STATUS);
+       dev_dbg(&sock->dev,
+               "data event: fifo_status %x, host_status %x, flags %x\n",
+               fifo_status, host_status, host->cmd_flags);
 
        if (host->req) {
-               if (fifo_status & TIFM_FIFO_READY) {
-                       if (!host->no_dma || tifm_ms_transfer_data(host, 0)) {
+               if (host->use_dma && (fifo_status & 1)) {
+                       host->cmd_flags |= FIFO_READY;
+                       rc = tifm_ms_check_status(host);
+               }
+               if (!host->use_dma && (fifo_status & TIFM_FIFO_MORE)) {
+                       if (!tifm_ms_transfer_data(host)) {
                                host->cmd_flags |= FIFO_READY;
                                rc = tifm_ms_check_status(host);
                        }
@@ -419,29 +428,22 @@ static void tifm_ms_card_event(struct tifm_dev *sock)
                host_status, host->cmd_flags);
 
        if (host->req) {
-               if (host_status & TIFM_MS_TIMEOUT)
+               if (host_status & TIFM_MS_STAT_TOE)
                        host->req->error = -ETIME;
-               else if (host_status & TIFM_MS_BADCRC)
+               else if (host_status & TIFM_MS_STAT_CRC)
                        host->req->error = -EILSEQ;
 
-               if (host->req->error) {
-                       writel(TIFM_FIFO_INT_SETALL,
-                              sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
-                       writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
-               }
-
-               if (host_status & TIFM_MS_EOTPC)
+               if (host_status & TIFM_MS_STAT_RDY)
                        host->cmd_flags |= CMD_READY;
-               if (host_status & TIFM_MS_INT)
-                       host->cmd_flags |= CARD_READY;
+
+               if (host_status & TIFM_MS_STAT_MSINT)
+                       host->cmd_flags |= CARD_INT;
 
                rc = tifm_ms_check_status(host);
 
        }
 
-       writel(TIFM_MS_SYS_NOT_RDY | readl(sock->addr + SOCK_MS_SYSTEM),
-              sock->addr + SOCK_MS_SYSTEM);
-       writel((~TIFM_MS_SYS_DATA) & readl(sock->addr + SOCK_MS_SYSTEM),
+       writel(TIFM_MS_SYS_INTCLR | readl(sock->addr + SOCK_MS_SYSTEM),
               sock->addr + SOCK_MS_SYSTEM);
 
        if (!rc)
@@ -497,15 +499,26 @@ static void tifm_ms_set_param(struct memstick_host *msh,
 
        switch (param) {
        case MEMSTICK_POWER:
-               /* this is set by card detection mechanism */
+               /* also affected by media detection mechanism */
+               if (value == MEMSTICK_POWER_ON) {
+                       host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI;
+                       writel(TIFM_MS_SYS_RESET, sock->addr + SOCK_MS_SYSTEM);
+                       writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR,
+                              sock->addr + SOCK_MS_SYSTEM);
+                       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
+               } else if (value == MEMSTICK_POWER_OFF) {
+                       writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR,
+                              sock->addr + SOCK_MS_SYSTEM);
+                       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
+               }
                break;
        case MEMSTICK_INTERFACE:
                if (value == MEMSTICK_SERIAL) {
-                       host->mode_mask = TIFM_MS_SERIAL;
+                       host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI;
                        writel((~TIFM_CTRL_FAST_CLK)
                               & readl(sock->addr + SOCK_CONTROL),
                               sock->addr + SOCK_CONTROL);
-               } else if (value == MEMSTICK_PARALLEL) {
+               } else if (value == MEMSTICK_PAR4) {
                        host->mode_mask = 0;
                        writel(TIFM_CTRL_FAST_CLK
                               | readl(sock->addr + SOCK_CONTROL),
@@ -532,21 +545,6 @@ static void tifm_ms_abort(unsigned long data)
        tifm_eject(host->dev);
 }
 
-static int tifm_ms_initialize_host(struct tifm_ms *host)
-{
-       struct tifm_dev *sock = host->dev;
-       struct memstick_host *msh = tifm_get_drvdata(sock);
-
-       host->mode_mask = TIFM_MS_SERIAL;
-       writel(0x8000, sock->addr + SOCK_MS_SYSTEM);
-       writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
-       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
-       if (tifm_has_ms_pif(sock))
-               msh->caps |= MEMSTICK_CAP_PARALLEL;
-
-       return 0;
-}
-
 static int tifm_ms_probe(struct tifm_dev *sock)
 {
        struct memstick_host *msh;
@@ -568,7 +566,6 @@ static int tifm_ms_probe(struct tifm_dev *sock)
        tifm_set_drvdata(sock, msh);
        host->dev = sock;
        host->timeout_jiffies = msecs_to_jiffies(1000);
-       host->no_dma = no_dma;
 
        setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host);
 
@@ -576,10 +573,10 @@ static int tifm_ms_probe(struct tifm_dev *sock)
        msh->set_param = tifm_ms_set_param;
        sock->card_event = tifm_ms_card_event;
        sock->data_event = tifm_ms_data_event;
-       rc = tifm_ms_initialize_host(host);
+       if (tifm_has_ms_pif(sock))
+               msh->caps |= MEMSTICK_CAP_PAR4;
 
-       if (!rc)
-               rc = memstick_add_host(msh);
+       rc = memstick_add_host(msh);
        if (!rc)
                return 0;
 
@@ -601,7 +598,7 @@ static void tifm_ms_remove(struct tifm_dev *sock)
                writel(TIFM_FIFO_INT_SETALL,
                       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
                writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
-               if ((host->req->io_type == MEMSTICK_IO_SG) && !host->no_dma)
+               if (host->use_dma)
                        tifm_unmap_sg(sock, &host->req->sg, 1,
                                      host->req->data_dir == READ
                                      ? PCI_DMA_TODEVICE
@@ -617,10 +614,6 @@ static void tifm_ms_remove(struct tifm_dev *sock)
        spin_unlock_irqrestore(&sock->lock, flags);
 
        memstick_remove_host(msh);
-
-       writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
-       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
-
        memstick_free_host(msh);
 }
 
@@ -628,17 +621,17 @@ static void tifm_ms_remove(struct tifm_dev *sock)
 
 static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state)
 {
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+
+       memstick_suspend_host(msh);
        return 0;
 }
 
 static int tifm_ms_resume(struct tifm_dev *sock)
 {
        struct memstick_host *msh = tifm_get_drvdata(sock);
-       struct tifm_ms *host = memstick_priv(msh);
-
-       tifm_ms_initialize_host(host);
-       memstick_detect_change(msh);
 
+       memstick_resume_host(msh);
        return 0;
 }
 
@@ -679,7 +672,6 @@ MODULE_AUTHOR("Alex Dubov");
 MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver");
 MODULE_LICENSE("GPL");
 MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl);
-MODULE_VERSION(DRIVER_VERSION);
 
 module_init(tifm_ms_init);
 module_exit(tifm_ms_exit);