aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkos Chandras <markos.chandras@imgtec.com>2013-03-12 06:53:13 -0400
committerChris Ball <cjb@laptop.org>2013-03-22 12:53:49 -0400
commitcfbeb59c7a818a2ec0008914ce479fe5d6f5978b (patch)
tree4da01e29b0597b53a573e1e403cbb9dfdcaabb25
parent3e4b0d8bdc6ddd68fdc1f4592822af2ae5cc2859 (diff)
mmc: dw_mmc: Handle unaligned data submission correctly
Commit f9c2a0dc42a6938ff2a80e55ca2bbd1d5581c72e "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: Markos Chandras <markos.chandras@imgtec.com> Acked-by: Jaehoon Chung <jh80.chung@samsung.com> Acked-by: Seungwon Jeon <tgih.jun@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r--drivers/mmc/host/dw_mmc.c33
1 files changed, 24 insertions, 9 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index a23b262f9cdd..a44382059540 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1199,12 +1199,15 @@ static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt)
1199 1199
1200static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) 1200static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
1201{ 1201{
1202 struct mmc_data *data = host->data;
1203 int init_cnt = cnt;
1204
1202 /* try and push anything in the part_buf */ 1205 /* try and push anything in the part_buf */
1203 if (unlikely(host->part_buf_count)) { 1206 if (unlikely(host->part_buf_count)) {
1204 int len = dw_mci_push_part_bytes(host, buf, cnt); 1207 int len = dw_mci_push_part_bytes(host, buf, cnt);
1205 buf += len; 1208 buf += len;
1206 cnt -= len; 1209 cnt -= len;
1207 if (!sg_next(host->sg) || host->part_buf_count == 2) { 1210 if (host->part_buf_count == 2) {
1208 mci_writew(host, DATA(host->data_offset), 1211 mci_writew(host, DATA(host->data_offset),
1209 host->part_buf16); 1212 host->part_buf16);
1210 host->part_buf_count = 0; 1213 host->part_buf_count = 0;
@@ -1237,9 +1240,11 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
1237 /* put anything remaining in the part_buf */ 1240 /* put anything remaining in the part_buf */
1238 if (cnt) { 1241 if (cnt) {
1239 dw_mci_set_part_bytes(host, buf, cnt); 1242 dw_mci_set_part_bytes(host, buf, cnt);
1240 if (!sg_next(host->sg)) 1243 /* Push data if we have reached the expected data length */
1244 if ((data->bytes_xfered + init_cnt) ==
1245 (data->blksz * data->blocks))
1241 mci_writew(host, DATA(host->data_offset), 1246 mci_writew(host, DATA(host->data_offset),
1242 host->part_buf16); 1247 host->part_buf16);
1243 } 1248 }
1244} 1249}
1245 1250
@@ -1277,12 +1282,15 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
1277 1282
1278static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) 1283static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
1279{ 1284{
1285 struct mmc_data *data = host->data;
1286 int init_cnt = cnt;
1287
1280 /* try and push anything in the part_buf */ 1288 /* try and push anything in the part_buf */
1281 if (unlikely(host->part_buf_count)) { 1289 if (unlikely(host->part_buf_count)) {
1282 int len = dw_mci_push_part_bytes(host, buf, cnt); 1290 int len = dw_mci_push_part_bytes(host, buf, cnt);
1283 buf += len; 1291 buf += len;
1284 cnt -= len; 1292 cnt -= len;
1285 if (!sg_next(host->sg) || host->part_buf_count == 4) { 1293 if (host->part_buf_count == 4) {
1286 mci_writel(host, DATA(host->data_offset), 1294 mci_writel(host, DATA(host->data_offset),
1287 host->part_buf32); 1295 host->part_buf32);
1288 host->part_buf_count = 0; 1296 host->part_buf_count = 0;
@@ -1315,9 +1323,11 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
1315 /* put anything remaining in the part_buf */ 1323 /* put anything remaining in the part_buf */
1316 if (cnt) { 1324 if (cnt) {
1317 dw_mci_set_part_bytes(host, buf, cnt); 1325 dw_mci_set_part_bytes(host, buf, cnt);
1318 if (!sg_next(host->sg)) 1326 /* Push data if we have reached the expected data length */
1327 if ((data->bytes_xfered + init_cnt) ==
1328 (data->blksz * data->blocks))
1319 mci_writel(host, DATA(host->data_offset), 1329 mci_writel(host, DATA(host->data_offset),
1320 host->part_buf32); 1330 host->part_buf32);
1321 } 1331 }
1322} 1332}
1323 1333
@@ -1355,12 +1365,15 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
1355 1365
1356static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) 1366static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
1357{ 1367{
1368 struct mmc_data *data = host->data;
1369 int init_cnt = cnt;
1370
1358 /* try and push anything in the part_buf */ 1371 /* try and push anything in the part_buf */
1359 if (unlikely(host->part_buf_count)) { 1372 if (unlikely(host->part_buf_count)) {
1360 int len = dw_mci_push_part_bytes(host, buf, cnt); 1373 int len = dw_mci_push_part_bytes(host, buf, cnt);
1361 buf += len; 1374 buf += len;
1362 cnt -= len; 1375 cnt -= len;
1363 if (!sg_next(host->sg) || host->part_buf_count == 8) { 1376 if (host->part_buf_count == 8) {
1364 mci_writew(host, DATA(host->data_offset), 1377 mci_writew(host, DATA(host->data_offset),
1365 host->part_buf); 1378 host->part_buf);
1366 host->part_buf_count = 0; 1379 host->part_buf_count = 0;
@@ -1393,9 +1406,11 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
1393 /* put anything remaining in the part_buf */ 1406 /* put anything remaining in the part_buf */
1394 if (cnt) { 1407 if (cnt) {
1395 dw_mci_set_part_bytes(host, buf, cnt); 1408 dw_mci_set_part_bytes(host, buf, cnt);
1396 if (!sg_next(host->sg)) 1409 /* Push data if we have reached the expected data length */
1410 if ((data->bytes_xfered + init_cnt) ==
1411 (data->blksz * data->blocks))
1397 mci_writeq(host, DATA(host->data_offset), 1412 mci_writeq(host, DATA(host->data_offset),
1398 host->part_buf); 1413 host->part_buf);
1399 } 1414 }
1400} 1415}
1401 1416