Merge branch 'core/xen' into x86/xen
[safe/jmp/linux-2.6] / drivers / mmc / card / block.c
index f9ad960..6986f39 100644 (file)
@@ -2,7 +2,7 @@
  * Block driver for media (i.e., flash cards)
  *
  * Copyright 2002 Hewlett-Packard Company
- * Copyright 2005-2007 Pierre Ossman
+ * Copyright 2005-2008 Pierre Ossman
  *
  * Use consistent with the GNU GPL is permitted,
  * provided that this copyright notice is
@@ -103,8 +103,10 @@ static int mmc_blk_open(struct inode *inode, struct file *filp)
                        check_disk_change(inode->i_bdev);
                ret = 0;
 
-               if ((filp->f_mode & FMODE_WRITE) && md->read_only)
+               if ((filp->f_mode & FMODE_WRITE) && md->read_only) {
+                       mmc_blk_put(md);
                        ret = -EROFS;
+               }
        }
 
        return ret;
@@ -213,7 +215,8 @@ 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 = 1, sg_pos, data_size;
+       int ret = 1, data_size, i;
+       struct scatterlist *sg;
 
        mmc_claim_host(card->host);
 
@@ -237,17 +240,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                if (brq.data.blocks > card->host->max_blk_count)
                        brq.data.blocks = card->host->max_blk_count;
 
-               /*
-                * If the host doesn't support multiple block writes, force
-                * block writes to single block. SD cards are excepted from
-                * this rule as they support querying the number of
-                * successfully written sectors.
-                */
-               if (rq_data_dir(req) != READ &&
-                   !(card->host->caps & MMC_CAP_MULTIWRITE) &&
-                   !mmc_card_sd(card))
-                       brq.data.blocks = 1;
-
                if (brq.data.blocks > 1) {
                        /* SPI multiblock writes terminate using a special
                         * token, not a STOP_TRANSMISSION request.
@@ -278,40 +270,46 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 
                mmc_queue_bounce_pre(mq);
 
+               /*
+                * Adjust the sg list so it is the same size as the
+                * request.
+                */
                if (brq.data.blocks !=
                    (req->nr_sectors >> (md->block_bits - 9))) {
                        data_size = brq.data.blocks * brq.data.blksz;
-                       for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) {
-                               data_size -= mq->sg[sg_pos].length;
+                       for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
+                               data_size -= sg->length;
                                if (data_size <= 0) {
-                                       mq->sg[sg_pos].length += data_size;
-                                       sg_pos++;
+                                       sg->length += data_size;
+                                       i++;
                                        break;
                                }
                        }
-                       brq.data.sg_len = sg_pos;
+                       brq.data.sg_len = i;
                }
 
                mmc_wait_for_req(card->host, &brq.mrq);
 
                mmc_queue_bounce_post(mq);
 
+               /*
+                * Check for errors here, but don't jump to cmd_err
+                * until later as we need to wait for the card to leave
+                * programming mode even when things go wrong.
+                */
                if (brq.cmd.error) {
                        printk(KERN_ERR "%s: error %d sending read/write command\n",
                               req->rq_disk->disk_name, brq.cmd.error);
-                       goto cmd_err;
                }
 
                if (brq.data.error) {
                        printk(KERN_ERR "%s: error %d transferring data\n",
                               req->rq_disk->disk_name, brq.data.error);
-                       goto cmd_err;
                }
 
                if (brq.stop.error) {
                        printk(KERN_ERR "%s: error %d sending stop command\n",
                               req->rq_disk->disk_name, brq.stop.error);
-                       goto cmd_err;
                }
 
                if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
@@ -344,6 +342,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 #endif
                }
 
+               if (brq.cmd.error || brq.data.error || brq.stop.error)
+                       goto cmd_err;
+
                /*
                 * A block was successfully transferred.
                 */
@@ -362,30 +363,32 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
         * mark the known good sectors as ok.
         *
         * If the card is not SD, we can still ok written sectors
-        * if the controller can do proper error reporting.
+        * as reported by the controller (which might be less than
+        * the real number of written sectors, but never more).
         *
         * For reads we just fail the entire chunk as that should
         * be safe in all cases.
         */
-       if (rq_data_dir(req) != READ && mmc_card_sd(card)) {
-               u32 blocks;
-               unsigned int bytes;
-
-               blocks = mmc_sd_num_wr_blocks(card);
-               if (blocks != (u32)-1) {
-                       if (card->csd.write_partial)
-                               bytes = blocks << md->block_bits;
-                       else
-                               bytes = blocks << 9;
+       if (rq_data_dir(req) != READ) {
+               if (mmc_card_sd(card)) {
+                       u32 blocks;
+                       unsigned int bytes;
+
+                       blocks = mmc_sd_num_wr_blocks(card);
+                       if (blocks != (u32)-1) {
+                               if (card->csd.write_partial)
+                                       bytes = blocks << md->block_bits;
+                               else
+                                       bytes = blocks << 9;
+                               spin_lock_irq(&md->lock);
+                               ret = __blk_end_request(req, 0, bytes);
+                               spin_unlock_irq(&md->lock);
+                       }
+               } else {
                        spin_lock_irq(&md->lock);
-                       ret = __blk_end_request(req, 0, bytes);
+                       ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
                        spin_unlock_irq(&md->lock);
                }
-       } else if (rq_data_dir(req) != READ &&
-                  (card->host->caps & MMC_CAP_MULTIWRITE)) {
-               spin_lock_irq(&md->lock);
-               ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
-               spin_unlock_irq(&md->lock);
        }
 
        mmc_release_host(card->host);