diff options
| -rw-r--r-- | drivers/mmc/host/dw_mmc.c | 144 | ||||
| -rw-r--r-- | include/linux/mmc/dw_mmc.h | 6 |
2 files changed, 79 insertions, 71 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); |
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index e8779c6d1759..aae5d1f1bb39 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #ifndef LINUX_MMC_DW_MMC_H | 14 | #ifndef LINUX_MMC_DW_MMC_H |
| 15 | #define LINUX_MMC_DW_MMC_H | 15 | #define LINUX_MMC_DW_MMC_H |
| 16 | 16 | ||
| 17 | #include <linux/scatterlist.h> | ||
| 18 | |||
| 17 | #define MAX_MCI_SLOTS 2 | 19 | #define MAX_MCI_SLOTS 2 |
| 18 | 20 | ||
| 19 | enum dw_mci_state { | 21 | enum dw_mci_state { |
| @@ -40,7 +42,7 @@ struct mmc_data; | |||
| 40 | * @lock: Spinlock protecting the queue and associated data. | 42 | * @lock: Spinlock protecting the queue and associated data. |
| 41 | * @regs: Pointer to MMIO registers. | 43 | * @regs: Pointer to MMIO registers. |
| 42 | * @sg: Scatterlist entry currently being processed by PIO code, if any. | 44 | * @sg: Scatterlist entry currently being processed by PIO code, if any. |
| 43 | * @pio_offset: Offset into the current scatterlist entry. | 45 | * @sg_miter: PIO mapping scatterlist iterator. |
| 44 | * @cur_slot: The slot which is currently using the controller. | 46 | * @cur_slot: The slot which is currently using the controller. |
| 45 | * @mrq: The request currently being processed on @cur_slot, | 47 | * @mrq: The request currently being processed on @cur_slot, |
| 46 | * or NULL if the controller is idle. | 48 | * or NULL if the controller is idle. |
| @@ -115,7 +117,7 @@ struct dw_mci { | |||
| 115 | void __iomem *regs; | 117 | void __iomem *regs; |
| 116 | 118 | ||
| 117 | struct scatterlist *sg; | 119 | struct scatterlist *sg; |
| 118 | unsigned int pio_offset; | 120 | struct sg_mapping_iter sg_miter; |
| 119 | 121 | ||
| 120 | struct dw_mci_slot *cur_slot; | 122 | struct dw_mci_slot *cur_slot; |
| 121 | struct mmc_request *mrq; | 123 | struct mmc_request *mrq; |
