diff options
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 144 |
1 files changed, 75 insertions, 69 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 0e342793ff14..8bec1c36b159 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/ioport.h> | 22 | #include <linux/ioport.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | #include <linux/scatterlist.h> | ||
26 | #include <linux/seq_file.h> | 25 | #include <linux/seq_file.h> |
27 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
28 | #include <linux/stat.h> | 27 | #include <linux/stat.h> |
@@ -502,8 +501,14 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) | |||
502 | host->dir_status = DW_MCI_SEND_STATUS; | 501 | host->dir_status = DW_MCI_SEND_STATUS; |
503 | 502 | ||
504 | if (dw_mci_submit_data_dma(host, data)) { | 503 | if (dw_mci_submit_data_dma(host, data)) { |
504 | int flags = SG_MITER_ATOMIC; | ||
505 | if (host->data->flags & MMC_DATA_READ) | ||
506 | flags |= SG_MITER_TO_SG; | ||
507 | else | ||
508 | flags |= SG_MITER_FROM_SG; | ||
509 | |||
510 | sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); | ||
505 | host->sg = data->sg; | 511 | host->sg = data->sg; |
506 | host->pio_offset = 0; | ||
507 | host->part_buf_start = 0; | 512 | host->part_buf_start = 0; |
508 | host->part_buf_count = 0; | 513 | host->part_buf_count = 0; |
509 | 514 | ||
@@ -972,6 +977,7 @@ static void dw_mci_tasklet_func(unsigned long priv) | |||
972 | * generates a block interrupt, hence setting | 977 | * generates a block interrupt, hence setting |
973 | * the scatter-gather pointer to NULL. | 978 | * the scatter-gather pointer to NULL. |
974 | */ | 979 | */ |
980 | sg_miter_stop(&host->sg_miter); | ||
975 | host->sg = NULL; | 981 | host->sg = NULL; |
976 | ctrl = mci_readl(host, CTRL); | 982 | ctrl = mci_readl(host, CTRL); |
977 | ctrl |= SDMMC_CTRL_FIFO_RESET; | 983 | ctrl |= SDMMC_CTRL_FIFO_RESET; |
@@ -1311,54 +1317,44 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) | |||
1311 | 1317 | ||
1312 | static void dw_mci_read_data_pio(struct dw_mci *host) | 1318 | static void dw_mci_read_data_pio(struct dw_mci *host) |
1313 | { | 1319 | { |
1314 | struct scatterlist *sg = host->sg; | 1320 | struct sg_mapping_iter *sg_miter = &host->sg_miter; |
1315 | void *buf = sg_virt(sg); | 1321 | void *buf; |
1316 | unsigned int offset = host->pio_offset; | 1322 | unsigned int offset; |
1317 | struct mmc_data *data = host->data; | 1323 | struct mmc_data *data = host->data; |
1318 | int shift = host->data_shift; | 1324 | int shift = host->data_shift; |
1319 | u32 status; | 1325 | u32 status; |
1320 | unsigned int nbytes = 0, len; | 1326 | unsigned int nbytes = 0, len; |
1327 | unsigned int remain, fcnt; | ||
1321 | 1328 | ||
1322 | do { | 1329 | do { |
1323 | len = host->part_buf_count + | 1330 | if (!sg_miter_next(sg_miter)) |
1324 | (SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift); | 1331 | goto done; |
1325 | if (offset + len <= sg->length) { | 1332 | |
1333 | host->sg = sg_miter->__sg; | ||
1334 | buf = sg_miter->addr; | ||
1335 | remain = sg_miter->length; | ||
1336 | offset = 0; | ||
1337 | |||
1338 | do { | ||
1339 | fcnt = (SDMMC_GET_FCNT(mci_readl(host, STATUS)) | ||
1340 | << shift) + host->part_buf_count; | ||
1341 | len = min(remain, fcnt); | ||
1342 | if (!len) | ||
1343 | break; | ||
1326 | dw_mci_pull_data(host, (void *)(buf + offset), len); | 1344 | dw_mci_pull_data(host, (void *)(buf + offset), len); |
1327 | |||
1328 | offset += len; | 1345 | offset += len; |
1329 | nbytes += len; | 1346 | nbytes += len; |
1330 | 1347 | remain -= len; | |
1331 | if (offset == sg->length) { | 1348 | } while (remain); |
1332 | flush_dcache_page(sg_page(sg)); | 1349 | sg_miter->consumed = offset; |
1333 | host->sg = sg = sg_next(sg); | ||
1334 | if (!sg) | ||
1335 | goto done; | ||
1336 | |||
1337 | offset = 0; | ||
1338 | buf = sg_virt(sg); | ||
1339 | } | ||
1340 | } else { | ||
1341 | unsigned int remaining = sg->length - offset; | ||
1342 | dw_mci_pull_data(host, (void *)(buf + offset), | ||
1343 | remaining); | ||
1344 | nbytes += remaining; | ||
1345 | |||
1346 | flush_dcache_page(sg_page(sg)); | ||
1347 | host->sg = sg = sg_next(sg); | ||
1348 | if (!sg) | ||
1349 | goto done; | ||
1350 | |||
1351 | offset = len - remaining; | ||
1352 | buf = sg_virt(sg); | ||
1353 | dw_mci_pull_data(host, buf, offset); | ||
1354 | nbytes += offset; | ||
1355 | } | ||
1356 | 1350 | ||
1357 | status = mci_readl(host, MINTSTS); | 1351 | status = mci_readl(host, MINTSTS); |
1358 | mci_writel(host, RINTSTS, SDMMC_INT_RXDR); | 1352 | mci_writel(host, RINTSTS, SDMMC_INT_RXDR); |
1359 | if (status & DW_MCI_DATA_ERROR_FLAGS) { | 1353 | if (status & DW_MCI_DATA_ERROR_FLAGS) { |
1360 | host->data_status = status; | 1354 | host->data_status = status; |
1361 | data->bytes_xfered += nbytes; | 1355 | data->bytes_xfered += nbytes; |
1356 | sg_miter_stop(sg_miter); | ||
1357 | host->sg = NULL; | ||
1362 | smp_wmb(); | 1358 | smp_wmb(); |
1363 | 1359 | ||
1364 | set_bit(EVENT_DATA_ERROR, &host->pending_events); | 1360 | set_bit(EVENT_DATA_ERROR, &host->pending_events); |
@@ -1367,65 +1363,66 @@ static void dw_mci_read_data_pio(struct dw_mci *host) | |||
1367 | return; | 1363 | return; |
1368 | } | 1364 | } |
1369 | } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ | 1365 | } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ |
1370 | host->pio_offset = offset; | ||
1371 | data->bytes_xfered += nbytes; | 1366 | data->bytes_xfered += nbytes; |
1367 | |||
1368 | if (!remain) { | ||
1369 | if (!sg_miter_next(sg_miter)) | ||
1370 | goto done; | ||
1371 | sg_miter->consumed = 0; | ||
1372 | } | ||
1373 | sg_miter_stop(sg_miter); | ||
1372 | return; | 1374 | return; |
1373 | 1375 | ||
1374 | done: | 1376 | done: |
1375 | data->bytes_xfered += nbytes; | 1377 | data->bytes_xfered += nbytes; |
1378 | sg_miter_stop(sg_miter); | ||
1379 | host->sg = NULL; | ||
1376 | smp_wmb(); | 1380 | smp_wmb(); |
1377 | set_bit(EVENT_XFER_COMPLETE, &host->pending_events); | 1381 | set_bit(EVENT_XFER_COMPLETE, &host->pending_events); |
1378 | } | 1382 | } |
1379 | 1383 | ||
1380 | static void dw_mci_write_data_pio(struct dw_mci *host) | 1384 | static void dw_mci_write_data_pio(struct dw_mci *host) |
1381 | { | 1385 | { |
1382 | struct scatterlist *sg = host->sg; | 1386 | struct sg_mapping_iter *sg_miter = &host->sg_miter; |
1383 | void *buf = sg_virt(sg); | 1387 | void *buf; |
1384 | unsigned int offset = host->pio_offset; | 1388 | unsigned int offset; |
1385 | struct mmc_data *data = host->data; | 1389 | struct mmc_data *data = host->data; |
1386 | int shift = host->data_shift; | 1390 | int shift = host->data_shift; |
1387 | u32 status; | 1391 | u32 status; |
1388 | unsigned int nbytes = 0, len; | 1392 | unsigned int nbytes = 0, len; |
1393 | unsigned int fifo_depth = host->fifo_depth; | ||
1394 | unsigned int remain, fcnt; | ||
1389 | 1395 | ||
1390 | do { | 1396 | do { |
1391 | len = ((host->fifo_depth - | 1397 | if (!sg_miter_next(sg_miter)) |
1392 | SDMMC_GET_FCNT(mci_readl(host, STATUS))) << shift) | 1398 | goto done; |
1393 | - host->part_buf_count; | 1399 | |
1394 | if (offset + len <= sg->length) { | 1400 | host->sg = sg_miter->__sg; |
1401 | buf = sg_miter->addr; | ||
1402 | remain = sg_miter->length; | ||
1403 | offset = 0; | ||
1404 | |||
1405 | do { | ||
1406 | fcnt = ((fifo_depth - | ||
1407 | SDMMC_GET_FCNT(mci_readl(host, STATUS))) | ||
1408 | << shift) - host->part_buf_count; | ||
1409 | len = min(remain, fcnt); | ||
1410 | if (!len) | ||
1411 | break; | ||
1395 | host->push_data(host, (void *)(buf + offset), len); | 1412 | host->push_data(host, (void *)(buf + offset), len); |
1396 | |||
1397 | offset += len; | 1413 | offset += len; |
1398 | nbytes += len; | 1414 | nbytes += len; |
1399 | if (offset == sg->length) { | 1415 | remain -= len; |
1400 | host->sg = sg = sg_next(sg); | 1416 | } while (remain); |
1401 | if (!sg) | 1417 | sg_miter->consumed = offset; |
1402 | goto done; | ||
1403 | |||
1404 | offset = 0; | ||
1405 | buf = sg_virt(sg); | ||
1406 | } | ||
1407 | } else { | ||
1408 | unsigned int remaining = sg->length - offset; | ||
1409 | |||
1410 | host->push_data(host, (void *)(buf + offset), | ||
1411 | remaining); | ||
1412 | nbytes += remaining; | ||
1413 | |||
1414 | host->sg = sg = sg_next(sg); | ||
1415 | if (!sg) | ||
1416 | goto done; | ||
1417 | |||
1418 | offset = len - remaining; | ||
1419 | buf = sg_virt(sg); | ||
1420 | host->push_data(host, (void *)buf, offset); | ||
1421 | nbytes += offset; | ||
1422 | } | ||
1423 | 1418 | ||
1424 | status = mci_readl(host, MINTSTS); | 1419 | status = mci_readl(host, MINTSTS); |
1425 | mci_writel(host, RINTSTS, SDMMC_INT_TXDR); | 1420 | mci_writel(host, RINTSTS, SDMMC_INT_TXDR); |
1426 | if (status & DW_MCI_DATA_ERROR_FLAGS) { | 1421 | if (status & DW_MCI_DATA_ERROR_FLAGS) { |
1427 | host->data_status = status; | 1422 | host->data_status = status; |
1428 | data->bytes_xfered += nbytes; | 1423 | data->bytes_xfered += nbytes; |
1424 | sg_miter_stop(sg_miter); | ||
1425 | host->sg = NULL; | ||
1429 | 1426 | ||
1430 | smp_wmb(); | 1427 | smp_wmb(); |
1431 | 1428 | ||
@@ -1435,12 +1432,20 @@ static void dw_mci_write_data_pio(struct dw_mci *host) | |||
1435 | return; | 1432 | return; |
1436 | } | 1433 | } |
1437 | } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ | 1434 | } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ |
1438 | host->pio_offset = offset; | ||
1439 | data->bytes_xfered += nbytes; | 1435 | data->bytes_xfered += nbytes; |
1436 | |||
1437 | if (!remain) { | ||
1438 | if (!sg_miter_next(sg_miter)) | ||
1439 | goto done; | ||
1440 | sg_miter->consumed = 0; | ||
1441 | } | ||
1442 | sg_miter_stop(sg_miter); | ||
1440 | return; | 1443 | return; |
1441 | 1444 | ||
1442 | done: | 1445 | done: |
1443 | data->bytes_xfered += nbytes; | 1446 | data->bytes_xfered += nbytes; |
1447 | sg_miter_stop(sg_miter); | ||
1448 | host->sg = NULL; | ||
1444 | smp_wmb(); | 1449 | smp_wmb(); |
1445 | set_bit(EVENT_XFER_COMPLETE, &host->pending_events); | 1450 | set_bit(EVENT_XFER_COMPLETE, &host->pending_events); |
1446 | } | 1451 | } |
@@ -1643,6 +1648,7 @@ static void dw_mci_work_routine_card(struct work_struct *work) | |||
1643 | * block interrupt, hence setting the | 1648 | * block interrupt, hence setting the |
1644 | * scatter-gather pointer to NULL. | 1649 | * scatter-gather pointer to NULL. |
1645 | */ | 1650 | */ |
1651 | sg_miter_stop(&host->sg_miter); | ||
1646 | host->sg = NULL; | 1652 | host->sg = NULL; |
1647 | 1653 | ||
1648 | ctrl = mci_readl(host, CTRL); | 1654 | ctrl = mci_readl(host, CTRL); |