Merge master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[powerpc.git] / drivers / mmc / mmc_block.c
index a0e0dad..c1293f1 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/protocol.h>
+#include <linux/mmc/host.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -157,14 +158,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 {
        struct mmc_blk_data *md = mq->data;
        struct mmc_card *card = md->queue.card;
+       struct mmc_blk_request brq;
        int ret;
 
        if (mmc_card_claim_host(card))
                goto cmd_err;
 
        do {
-               struct mmc_blk_request brq;
                struct mmc_command cmd;
+               u32 readcmd, writecmd;
 
                memset(&brq, 0, sizeof(struct mmc_blk_request));
                brq.mrq.cmd = &brq.cmd;
@@ -172,7 +174,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 
                brq.cmd.arg = req->sector << 9;
                brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
-               brq.data.blksz_bits = md->block_bits;
                brq.data.blksz = 1 << md->block_bits;
                brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
                brq.stop.opcode = MMC_STOP_TRANSMISSION;
@@ -181,20 +182,31 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 
                mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ);
 
-               if (rq_data_dir(req) == READ) {
-                       brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
-                       brq.data.flags |= MMC_DATA_READ;
-               } else {
-                       brq.cmd.opcode = MMC_WRITE_BLOCK;
-                       brq.data.flags |= MMC_DATA_WRITE;
+               /*
+                * If the host doesn't support multiple block writes, force
+                * block writes to single block.
+                */
+               if (rq_data_dir(req) != READ &&
+                   !(card->host->caps & MMC_CAP_MULTIWRITE))
                        brq.data.blocks = 1;
-               }
 
                if (brq.data.blocks > 1) {
                        brq.data.flags |= MMC_DATA_MULTI;
                        brq.mrq.stop = &brq.stop;
+                       readcmd = MMC_READ_MULTIPLE_BLOCK;
+                       writecmd = MMC_WRITE_MULTIPLE_BLOCK;
                } else {
                        brq.mrq.stop = NULL;
+                       readcmd = MMC_READ_SINGLE_BLOCK;
+                       writecmd = MMC_WRITE_BLOCK;
+               }
+
+               if (rq_data_dir(req) == READ) {
+                       brq.cmd.opcode = readcmd;
+                       brq.data.flags |= MMC_DATA_READ;
+               } else {
+                       brq.cmd.opcode = writecmd;
+                       brq.data.flags |= MMC_DATA_WRITE;
                }
 
                brq.data.sg = mq->sg;
@@ -219,27 +231,29 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                        goto cmd_err;
                }
 
-               do {
-                       int err;
-
-                       cmd.opcode = MMC_SEND_STATUS;
-                       cmd.arg = card->rca << 16;
-                       cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
-                       err = mmc_wait_for_cmd(card->host, &cmd, 5);
-                       if (err) {
-                               printk(KERN_ERR "%s: error %d requesting status\n",
-                                      req->rq_disk->disk_name, err);
-                               goto cmd_err;
-                       }
-               } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+               if (rq_data_dir(req) != READ) {
+                       do {
+                               int err;
+
+                               cmd.opcode = MMC_SEND_STATUS;
+                               cmd.arg = card->rca << 16;
+                               cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+                               err = mmc_wait_for_cmd(card->host, &cmd, 5);
+                               if (err) {
+                                       printk(KERN_ERR "%s: error %d requesting status\n",
+                                              req->rq_disk->disk_name, err);
+                                       goto cmd_err;
+                               }
+                       } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
 
 #if 0
-               if (cmd.resp[0] & ~0x00000900)
-                       printk(KERN_ERR "%s: status = %08x\n",
-                              req->rq_disk->disk_name, cmd.resp[0]);
-               if (mmc_decode_status(cmd.resp))
-                       goto cmd_err;
+                       if (cmd.resp[0] & ~0x00000900)
+                               printk(KERN_ERR "%s: status = %08x\n",
+                                      req->rq_disk->disk_name, cmd.resp[0]);
+                       if (mmc_decode_status(cmd.resp))
+                               goto cmd_err;
 #endif
+               }
 
                /*
                 * A block was successfully transferred.
@@ -264,17 +278,27 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
  cmd_err:
        mmc_card_release_host(card);
 
+       ret = 1;
+
        /*
-        * This is a little draconian, but until we get proper
-        * error handling sorted out here, its the best we can
-        * do - especially as some hosts have no idea how much
-        * data was transferred before the error occurred.
+        * For writes and where the host claims to support proper
+        * error reporting, we first ok the successful blocks.
+        *
+        * For reads we just fail the entire chunk as that should
+        * be safe in all cases.
         */
+       if (rq_data_dir(req) != READ &&
+           (card->host->caps & MMC_CAP_MULTIWRITE)) {
+               spin_lock_irq(&md->lock);
+               ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered);
+               spin_unlock_irq(&md->lock);
+       }
+
        spin_lock_irq(&md->lock);
-       do {
+       while (ret) {
                ret = end_that_request_chunk(req, 0,
                                req->current_nr_sectors << 9);
-       } while (ret);
+       }
 
        add_disk_randomness(req->rq_disk);
        blkdev_dequeue_request(req);