OSDN Git Service

mmc: cmdq_hci: Fix ADMA error issue
authorSahitya Tummala <stummala@codeaurora.org>
Wed, 30 Sep 2015 10:25:41 +0000 (15:55 +0530)
committerSubhash Jadavani <subhashj@codeaurora.org>
Tue, 31 May 2016 22:27:35 +0000 (15:27 -0700)
The controller triggers an ADMA error when ADMA engine is configured
and used in 32-bit mode. This is because the current code always
writes 64-bit address to 32-bit address field of a transfer
descriptor (bits [63:32]). This corrupts the first 32-bit value
of the next transfer descriptor.

Below scenario describes how ADMA error can happen -

1. Req#1 - uses slot 1, prepares it's descriptors, queues to the controller
2. Req#2 - uses slot 0, prepares max descriptors (cq_host->mmc->max_segs).
3. Req#1 gets ADMA error from the controller.

At step 2, when it prepares the last transfer descriptor (max_segs), it
overwrites the 32-bit address field with a 64-bit address and thus corrupts
the first entry of slot 1 transfer descriptor.

Change-Id: I3eb2dbb40c76ec77626f647d6ec24df4a0858fcb
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
drivers/mmc/host/cmdq_hci.c

index a2c2f7b..b29472a 100644 (file)
@@ -468,10 +468,9 @@ static int cmdq_dma_map(struct mmc_host *host, struct mmc_request *mrq)
        return sg_count;
 }
 
-static void cmdq_set_tran_desc(u8 *desc,
-                                dma_addr_t addr, int len, bool end)
+static void cmdq_set_tran_desc(u8 *desc, dma_addr_t addr, int len,
+                               bool end, bool is_dma64)
 {
-       __le64 *dataddr = (__le64 __force *)(desc + 4);
        __le32 *attr = (__le32 __force *)desc;
 
        *attr = (VALID(1) |
@@ -480,7 +479,15 @@ static void cmdq_set_tran_desc(u8 *desc,
                 ACT(0x4) |
                 DAT_LENGTH(len));
 
-       dataddr[0] = cpu_to_le64(addr);
+       if (is_dma64) {
+               __le64 *dataddr = (__le64 __force *)(desc + 4);
+
+               dataddr[0] = cpu_to_le64(addr);
+       } else {
+               __le32 *dataddr = (__le32 __force *)(desc + 4);
+
+               dataddr[0] = cpu_to_le32(addr);
+       }
 }
 
 static int cmdq_prep_tran_desc(struct mmc_request *mrq,
@@ -509,7 +516,7 @@ static int cmdq_prep_tran_desc(struct mmc_request *mrq,
 
                if ((i+1) == sg_count)
                        end = true;
-               cmdq_set_tran_desc(desc, addr, len, end);
+               cmdq_set_tran_desc(desc, addr, len, end, cq_host->dma64);
                desc += cq_host->trans_desc_len;
        }