mmc: core: add workaround for controllers with broken multiblock reads
authorPaul Walmsley <paul@pwsan.com>
Thu, 6 Oct 2011 20:50:33 +0000 (14:50 -0600)
committerChris Ball <cjb@laptop.org>
Thu, 27 Oct 2011 13:10:57 +0000 (09:10 -0400)
Due to hardware bugs, some MMC host controllers don't support
multiple-block reads[1].  To resolve, add a new MMC capability flag,
MMC_CAP2_NO_MULTI_READ, which can be set by affected host controller
drivers.  When this capability is set, all reads will be issued one
sector at a time.

1. See for example Advisory 2.1.1.128 "MMC: Multiple Block Read
Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
Revision F (October 2010) (SPRZ278F), available from
http://focus.ti.com/lit/er/sprz278f/sprz278f.pdf

Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Dave Hylands <dhylands@gmail.com>
Tested-by: Steve Sakoman <sakoman@gmail.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/card/block.c
include/linux/mmc/host.h

index c0cb225bbb4734171322be462f86d4d5f725216e..a1cb21f95302c497157dc97ed85f9acd68a3c10d 100644 (file)
@@ -1030,13 +1030,20 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
        if (brq->data.blocks > card->host->max_blk_count)
                brq->data.blocks = card->host->max_blk_count;
 
-       /*
-        * After a read error, we redo the request one sector at a time
-        * in order to accurately determine which sectors can be read
-        * successfully.
-        */
-       if (disable_multi && brq->data.blocks > 1)
-               brq->data.blocks = 1;
+       if (brq->data.blocks > 1) {
+               /*
+                * After a read error, we redo the request one sector
+                * at a time in order to accurately determine which
+                * sectors can be read successfully.
+                */
+               if (disable_multi)
+                       brq->data.blocks = 1;
+
+               /* Some controllers can't do multiblock reads due to hw bugs */
+               if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ &&
+                   rq_data_dir(req) == READ)
+                       brq->data.blocks = 1;
+       }
 
        if (brq->data.blocks > 1 || do_rel_wr) {
                /* SPI multiblock writes terminate using a special
index 16e9338944e82354d3d8e0d866619db98c61f1ee..a3ac9c48e5de60dc71fe71f3eb5d067e315ac4fb 100644 (file)
@@ -241,6 +241,7 @@ struct mmc_host {
 #define MMC_CAP2_BOOTPART_NOACC        (1 << 0)        /* Boot partition no access */
 #define MMC_CAP2_CACHE_CTRL    (1 << 1)        /* Allow cache control */
 #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)      /* Notify poweroff supported */
+#define MMC_CAP2_NO_MULTI_READ (1 << 3)        /* Multiblock reads don't work */
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */
        unsigned int        power_notify_type;