/*
  *  linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver
  *
- *  Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved.
+ *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
                        chunk_remain = min(blksize, 4);
                }
 
-               size = min(host->size, host->remain);
-               size = min(size, chunk_remain);
+               size = min(host->remain, chunk_remain);
 
                chunk_remain -= size;
                blksize -= size;
                host->offset += size;
                host->remain -= size;
-               host->size -= size;
+
                while (size) {
                        *buffer = data & 0xFF;
                        buffer++;
        buffer = sdhci_sg_to_buffer(host) + host->offset;
 
        while (blksize) {
-               size = min(host->size, host->remain);
-               size = min(size, chunk_remain);
+               size = min(host->remain, chunk_remain);
 
                chunk_remain -= size;
                blksize -= size;
                host->offset += size;
                host->remain -= size;
-               host->size -= size;
+
                while (size) {
                        data >>= 8;
                        data |= (u32)*buffer << 24;
 
        BUG_ON(!host->data);
 
-       if (host->size == 0)
+       if (host->num_sg == 0)
                return;
 
        if (host->data->flags & MMC_DATA_READ)
                else
                        sdhci_write_block_pio(host);
 
-               if (host->size == 0)
+               if (host->num_sg == 0)
                        break;
-
-               BUG_ON(host->num_sg == 0);
        }
 
        DBG("PIO transfer complete.\n");
 
                writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
        } else {
-               host->size = data->blksz * data->blocks;
-
                host->cur_sg = data->sg;
                host->num_sg = data->sg_len;
 
                        "though there were blocks left.\n",
                        mmc_hostname(host->mmc));
                data->error = MMC_ERR_FAILED;
-       } else if (host->size != 0) {
-               printk(KERN_ERR "%s: %d bytes were left untransferred.\n",
-                       mmc_hostname(host->mmc), host->size);
-               data->error = MMC_ERR_FAILED;
        }
 
        DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered);
 
 /*
  *  linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
  *
- *  Copyright (C) 2004-2006 Pierre Ossman, All Rights Reserved.
+ *  Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
 {
-       unsigned int len, i, size;
+       unsigned int len, i;
        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++) {
                sgbuf = page_address(sg[i].page) + sg[i].offset;
-               if (size < sg[i].length)
-                       memcpy(dmabuf, sgbuf, size);
-               else
-                       memcpy(dmabuf, sgbuf, sg[i].length);
+               memcpy(dmabuf, sgbuf, sg[i].length);
                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)
 {
-       unsigned int len, i, size;
+       unsigned int len, i;
        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++) {
                sgbuf = page_address(sg[i].page) + sg[i].offset;
-               if (size < sg[i].length)
-                       memcpy(sgbuf, dmabuf, size);
-               else
-                       memcpy(sgbuf, dmabuf, sg[i].length);
+               memcpy(sgbuf, dmabuf, sg[i].length);
                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;
 }
 
 /*
        /*
         * Handle excessive data.
         */
-       if (data->bytes_xfered == host->size)
+       if (host->num_sg == 0)
                return;
 
        buffer = wbsd_sg_to_buffer(host) + host->offset;
 
                        data->bytes_xfered++;
 
-                       /*
-                        * Transfer done?
-                        */
-                       if (data->bytes_xfered == host->size)
-                               return;
-
                        /*
                         * End of scatter list entry?
                         */
                                /*
                                 * Get next entry. Check if last.
                                 */
-                               if (!wbsd_next_sg(host)) {
-                                       /*
-                                        * We should never reach this point.
-                                        * It means that we're trying to
-                                        * transfer more blocks than can fit
-                                        * into the scatter list.
-                                        */
-                                       BUG_ON(1);
-
-                                       host->size = data->bytes_xfered;
-
+                               if (!wbsd_next_sg(host))
                                        return;
-                               }
 
                                buffer = wbsd_sg_to_buffer(host);
                        }
         * hardware problem. The chip doesn't trigger
         * FIFO threshold interrupts properly.
         */
-       if ((host->size - data->bytes_xfered) < 16)
+       if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
                tasklet_schedule(&host->fifo_tasklet);
 }
 
         * Check that we aren't being called after the
         * entire buffer has been transfered.
         */
-       if (data->bytes_xfered == host->size)
+       if (host->num_sg == 0)
                return;
 
        buffer = wbsd_sg_to_buffer(host) + host->offset;
 
                        data->bytes_xfered++;
 
-                       /*
-                        * Transfer done?
-                        */
-                       if (data->bytes_xfered == host->size)
-                               return;
-
                        /*
                         * End of scatter list entry?
                         */
                                /*
                                 * Get next entry. Check if last.
                                 */
-                               if (!wbsd_next_sg(host)) {
-                                       /*
-                                        * We should never reach this point.
-                                        * It means that we're trying to
-                                        * transfer more blocks than can fit
-                                        * into the scatter list.
-                                        */
-                                       BUG_ON(1);
-
-                                       host->size = data->bytes_xfered;
-
+                               if (!wbsd_next_sg(host))
                                        return;
-                               }
 
                                buffer = wbsd_sg_to_buffer(host);
                        }
        u16 blksize;
        u8 setup;
        unsigned long dmaflags;
+       unsigned int size;
 
        DBGF("blksz %04x blks %04x flags %08x\n",
                data->blksz, data->blocks, data->flags);
        /*
         * Calculate size.
         */
-       host->size = data->blocks * data->blksz;
+       size = data->blocks * data->blksz;
 
        /*
         * Check timeout values for overflow.
                /*
                 * The buffer for DMA is only 64 kB.
                 */
-               BUG_ON(host->size > 0x10000);
-               if (host->size > 0x10000) {
+               BUG_ON(size > 0x10000);
+               if (size > 0x10000) {
                        data->error = MMC_ERR_INVALID;
                        return;
                }
                else
                        set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
                set_dma_addr(host->dma, host->dma_addr);
-               set_dma_count(host->dma, host->size);
+               set_dma_count(host->dma, size);
 
                enable_dma(host->dma);
                release_dma_lock(dmaflags);
                count = get_dma_residue(host->dma);
                release_dma_lock(dmaflags);
 
+               data->bytes_xfered = host->mrq->data->blocks *
+                       host->mrq->data->blksz - count;
+               data->bytes_xfered -= data->bytes_xfered % data->blksz;
+
                /*
                 * Any leftover data?
                 */
                                "%d bytes left.\n",
                                mmc_hostname(host->mmc), count);
 
-                       data->error = MMC_ERR_FAILED;
+                       if (data->error == MMC_ERR_NONE)
+                               data->error = MMC_ERR_FAILED;
                } else {
                        /*
                         * Transfer data from DMA buffer to
                         */
                        if (data->flags & MMC_DATA_READ)
                                wbsd_dma_to_sg(host, data);
+               }
 
-                       data->bytes_xfered = host->size;
+               if (data->error != MMC_ERR_NONE) {
+                       if (data->bytes_xfered)
+                               data->bytes_xfered -= data->blksz;
                }
        }
 
        /*
         * Done?
         */
-       if (host->size == data->bytes_xfered) {
+       if (host->num_sg == 0) {
                wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
                tasklet_schedule(&host->finish_tasklet);
        }