Commit cfbeb59c authored by Markos Chandras's avatar Markos Chandras Committed by Chris Ball

mmc: dw_mmc: Handle unaligned data submission correctly

Commit f9c2a0dc
"mmc: dw_mmc: Fix PIO mode with support of highmem" introduced
a regression since v3.2 making the mmc_test hang on test #13
with a "Data starvation by host timeout" interrupt.

This is because, sg_mapping_iter is used to iterate through the
data which spans on multiple pages. The problem is detected on
unaligned data submission where the code previously checked for
!(sg_next(host->sg)) which is true because we only have a single
scatter/gather list which then expands to multiple pages.
Therefore, the driver incorrectly assumed that this was the last
list item and submitted unaligned data to the mmc device. This
overflowed the FIFO on the device before all the data were written
to it. The code was fixed to only submit unaligned data when we are
handling the last sg_miter item by checking whether we reached
the desired data length or not.

The patch was tested against mmc_test and all the tests passed.
Signed-off-by: default avatarMarkos Chandras <markos.chandras@imgtec.com>
Acked-by: default avatarJaehoon Chung <jh80.chung@samsung.com>
Acked-by: default avatarSeungwon Jeon <tgih.jun@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 3e4b0d8b
...@@ -1199,12 +1199,15 @@ static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt) ...@@ -1199,12 +1199,15 @@ static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt)
static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
{ {
struct mmc_data *data = host->data;
int init_cnt = cnt;
/* try and push anything in the part_buf */ /* try and push anything in the part_buf */
if (unlikely(host->part_buf_count)) { if (unlikely(host->part_buf_count)) {
int len = dw_mci_push_part_bytes(host, buf, cnt); int len = dw_mci_push_part_bytes(host, buf, cnt);
buf += len; buf += len;
cnt -= len; cnt -= len;
if (!sg_next(host->sg) || host->part_buf_count == 2) { if (host->part_buf_count == 2) {
mci_writew(host, DATA(host->data_offset), mci_writew(host, DATA(host->data_offset),
host->part_buf16); host->part_buf16);
host->part_buf_count = 0; host->part_buf_count = 0;
...@@ -1237,9 +1240,11 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) ...@@ -1237,9 +1240,11 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
/* put anything remaining in the part_buf */ /* put anything remaining in the part_buf */
if (cnt) { if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt); dw_mci_set_part_bytes(host, buf, cnt);
if (!sg_next(host->sg)) /* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
mci_writew(host, DATA(host->data_offset), mci_writew(host, DATA(host->data_offset),
host->part_buf16); host->part_buf16);
} }
} }
...@@ -1277,12 +1282,15 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) ...@@ -1277,12 +1282,15 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
{ {
struct mmc_data *data = host->data;
int init_cnt = cnt;
/* try and push anything in the part_buf */ /* try and push anything in the part_buf */
if (unlikely(host->part_buf_count)) { if (unlikely(host->part_buf_count)) {
int len = dw_mci_push_part_bytes(host, buf, cnt); int len = dw_mci_push_part_bytes(host, buf, cnt);
buf += len; buf += len;
cnt -= len; cnt -= len;
if (!sg_next(host->sg) || host->part_buf_count == 4) { if (host->part_buf_count == 4) {
mci_writel(host, DATA(host->data_offset), mci_writel(host, DATA(host->data_offset),
host->part_buf32); host->part_buf32);
host->part_buf_count = 0; host->part_buf_count = 0;
...@@ -1315,9 +1323,11 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) ...@@ -1315,9 +1323,11 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
/* put anything remaining in the part_buf */ /* put anything remaining in the part_buf */
if (cnt) { if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt); dw_mci_set_part_bytes(host, buf, cnt);
if (!sg_next(host->sg)) /* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
mci_writel(host, DATA(host->data_offset), mci_writel(host, DATA(host->data_offset),
host->part_buf32); host->part_buf32);
} }
} }
...@@ -1355,12 +1365,15 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) ...@@ -1355,12 +1365,15 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
{ {
struct mmc_data *data = host->data;
int init_cnt = cnt;
/* try and push anything in the part_buf */ /* try and push anything in the part_buf */
if (unlikely(host->part_buf_count)) { if (unlikely(host->part_buf_count)) {
int len = dw_mci_push_part_bytes(host, buf, cnt); int len = dw_mci_push_part_bytes(host, buf, cnt);
buf += len; buf += len;
cnt -= len; cnt -= len;
if (!sg_next(host->sg) || host->part_buf_count == 8) { if (host->part_buf_count == 8) {
mci_writew(host, DATA(host->data_offset), mci_writew(host, DATA(host->data_offset),
host->part_buf); host->part_buf);
host->part_buf_count = 0; host->part_buf_count = 0;
...@@ -1393,9 +1406,11 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) ...@@ -1393,9 +1406,11 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
/* put anything remaining in the part_buf */ /* put anything remaining in the part_buf */
if (cnt) { if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt); dw_mci_set_part_bytes(host, buf, cnt);
if (!sg_next(host->sg)) /* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
mci_writeq(host, DATA(host->data_offset), mci_writeq(host, DATA(host->data_offset),
host->part_buf); host->part_buf);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment