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_dat | ||
