diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/dma/coh901318.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'drivers/dma/coh901318.c')
-rw-r--r-- | drivers/dma/coh901318.c | 136 |
1 files changed, 93 insertions, 43 deletions
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index aa384e53b7a..4234f416ef1 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <mach/coh901318.h> | 24 | #include <mach/coh901318.h> |
25 | 25 | ||
26 | #include "coh901318_lli.h" | 26 | #include "coh901318_lli.h" |
27 | #include "dmaengine.h" | ||
28 | 27 | ||
29 | #define COHC_2_DEV(cohc) (&cohc->chan.dev->device) | 28 | #define COHC_2_DEV(cohc) (&cohc->chan.dev->device) |
30 | 29 | ||
@@ -40,7 +39,7 @@ struct coh901318_desc { | |||
40 | struct scatterlist *sg; | 39 | struct scatterlist *sg; |
41 | unsigned int sg_len; | 40 | unsigned int sg_len; |
42 | struct coh901318_lli *lli; | 41 | struct coh901318_lli *lli; |
43 | enum dma_transfer_direction dir; | 42 | enum dma_data_direction dir; |
44 | unsigned long flags; | 43 | unsigned long flags; |
45 | u32 head_config; | 44 | u32 head_config; |
46 | u32 head_ctrl; | 45 | u32 head_ctrl; |
@@ -60,6 +59,7 @@ struct coh901318_base { | |||
60 | struct coh901318_chan { | 59 | struct coh901318_chan { |
61 | spinlock_t lock; | 60 | spinlock_t lock; |
62 | int allocated; | 61 | int allocated; |
62 | int completed; | ||
63 | int id; | 63 | int id; |
64 | int stopped; | 64 | int stopped; |
65 | 65 | ||
@@ -104,6 +104,13 @@ static void coh901318_list_print(struct coh901318_chan *cohc, | |||
104 | static struct coh901318_base *debugfs_dma_base; | 104 | static struct coh901318_base *debugfs_dma_base; |
105 | static struct dentry *dma_dentry; | 105 | static struct dentry *dma_dentry; |
106 | 106 | ||
107 | static int coh901318_debugfs_open(struct inode *inode, struct file *file) | ||
108 | { | ||
109 | |||
110 | file->private_data = inode->i_private; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
107 | static int coh901318_debugfs_read(struct file *file, char __user *buf, | 114 | static int coh901318_debugfs_read(struct file *file, char __user *buf, |
108 | size_t count, loff_t *f_pos) | 115 | size_t count, loff_t *f_pos) |
109 | { | 116 | { |
@@ -151,7 +158,7 @@ static int coh901318_debugfs_read(struct file *file, char __user *buf, | |||
151 | 158 | ||
152 | static const struct file_operations coh901318_debugfs_status_operations = { | 159 | static const struct file_operations coh901318_debugfs_status_operations = { |
153 | .owner = THIS_MODULE, | 160 | .owner = THIS_MODULE, |
154 | .open = simple_open, | 161 | .open = coh901318_debugfs_open, |
155 | .read = coh901318_debugfs_read, | 162 | .read = coh901318_debugfs_read, |
156 | .llseek = default_llseek, | 163 | .llseek = default_llseek, |
157 | }; | 164 | }; |
@@ -311,6 +318,20 @@ static int coh901318_prep_linked_list(struct coh901318_chan *cohc, | |||
311 | 318 | ||
312 | return 0; | 319 | return 0; |
313 | } | 320 | } |
321 | static dma_cookie_t | ||
322 | coh901318_assign_cookie(struct coh901318_chan *cohc, | ||
323 | struct coh901318_desc *cohd) | ||
324 | { | ||
325 | dma_cookie_t cookie = cohc->chan.cookie; | ||
326 | |||
327 | if (++cookie < 0) | ||
328 | cookie = 1; | ||
329 | |||
330 | cohc->chan.cookie = cookie; | ||
331 | cohd->desc.cookie = cookie; | ||
332 | |||
333 | return cookie; | ||
334 | } | ||
314 | 335 | ||
315 | static struct coh901318_desc * | 336 | static struct coh901318_desc * |
316 | coh901318_desc_get(struct coh901318_chan *cohc) | 337 | coh901318_desc_get(struct coh901318_chan *cohc) |
@@ -684,7 +705,7 @@ static void dma_tasklet(unsigned long data) | |||
684 | callback_param = cohd_fin->desc.callback_param; | 705 | callback_param = cohd_fin->desc.callback_param; |
685 | 706 | ||
686 | /* sign this job as completed on the channel */ | 707 | /* sign this job as completed on the channel */ |
687 | dma_cookie_complete(&cohd_fin->desc); | 708 | cohc->completed = cohd_fin->desc.cookie; |
688 | 709 | ||
689 | /* release the lli allocation and remove the descriptor */ | 710 | /* release the lli allocation and remove the descriptor */ |
690 | coh901318_lli_free(&cohc->base->pool, &cohd_fin->lli); | 711 | coh901318_lli_free(&cohc->base->pool, &cohd_fin->lli); |
@@ -908,7 +929,7 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan) | |||
908 | coh901318_config(cohc, NULL); | 929 | coh901318_config(cohc, NULL); |
909 | 930 | ||
910 | cohc->allocated = 1; | 931 | cohc->allocated = 1; |
911 | dma_cookie_init(chan); | 932 | cohc->completed = chan->cookie = 1; |
912 | 933 | ||
913 | spin_unlock_irqrestore(&cohc->lock, flags); | 934 | spin_unlock_irqrestore(&cohc->lock, flags); |
914 | 935 | ||
@@ -945,16 +966,16 @@ coh901318_tx_submit(struct dma_async_tx_descriptor *tx) | |||
945 | desc); | 966 | desc); |
946 | struct coh901318_chan *cohc = to_coh901318_chan(tx->chan); | 967 | struct coh901318_chan *cohc = to_coh901318_chan(tx->chan); |
947 | unsigned long flags; | 968 | unsigned long flags; |
948 | dma_cookie_t cookie; | ||
949 | 969 | ||
950 | spin_lock_irqsave(&cohc->lock, flags); | 970 | spin_lock_irqsave(&cohc->lock, flags); |
951 | cookie = dma_cookie_assign(tx); | 971 | |
972 | tx->cookie = coh901318_assign_cookie(cohc, cohd); | ||
952 | 973 | ||
953 | coh901318_desc_queue(cohc, cohd); | 974 | coh901318_desc_queue(cohc, cohd); |
954 | 975 | ||
955 | spin_unlock_irqrestore(&cohc->lock, flags); | 976 | spin_unlock_irqrestore(&cohc->lock, flags); |
956 | 977 | ||
957 | return cookie; | 978 | return tx->cookie; |
958 | } | 979 | } |
959 | 980 | ||
960 | static struct dma_async_tx_descriptor * | 981 | static struct dma_async_tx_descriptor * |
@@ -1013,8 +1034,8 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, | |||
1013 | 1034 | ||
1014 | static struct dma_async_tx_descriptor * | 1035 | static struct dma_async_tx_descriptor * |
1015 | coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | 1036 | coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, |
1016 | unsigned int sg_len, enum dma_transfer_direction direction, | 1037 | unsigned int sg_len, enum dma_data_direction direction, |
1017 | unsigned long flags, void *context) | 1038 | unsigned long flags) |
1018 | { | 1039 | { |
1019 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | 1040 | struct coh901318_chan *cohc = to_coh901318_chan(chan); |
1020 | struct coh901318_lli *lli; | 1041 | struct coh901318_lli *lli; |
@@ -1033,7 +1054,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
1033 | 1054 | ||
1034 | if (!sgl) | 1055 | if (!sgl) |
1035 | goto out; | 1056 | goto out; |
1036 | if (sg_dma_len(sgl) == 0) | 1057 | if (sgl->length == 0) |
1037 | goto out; | 1058 | goto out; |
1038 | 1059 | ||
1039 | spin_lock_irqsave(&cohc->lock, flg); | 1060 | spin_lock_irqsave(&cohc->lock, flg); |
@@ -1056,7 +1077,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
1056 | ctrl_last |= cohc->runtime_ctrl; | 1077 | ctrl_last |= cohc->runtime_ctrl; |
1057 | ctrl |= cohc->runtime_ctrl; | 1078 | ctrl |= cohc->runtime_ctrl; |
1058 | 1079 | ||
1059 | if (direction == DMA_MEM_TO_DEV) { | 1080 | if (direction == DMA_TO_DEVICE) { |
1060 | u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE | | 1081 | u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE | |
1061 | COH901318_CX_CTRL_SRC_ADDR_INC_ENABLE; | 1082 | COH901318_CX_CTRL_SRC_ADDR_INC_ENABLE; |
1062 | 1083 | ||
@@ -1064,7 +1085,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
1064 | ctrl_chained |= tx_flags; | 1085 | ctrl_chained |= tx_flags; |
1065 | ctrl_last |= tx_flags; | 1086 | ctrl_last |= tx_flags; |
1066 | ctrl |= tx_flags; | 1087 | ctrl |= tx_flags; |
1067 | } else if (direction == DMA_DEV_TO_MEM) { | 1088 | } else if (direction == DMA_FROM_DEVICE) { |
1068 | u32 rx_flags = COH901318_CX_CTRL_PRDD_DEST | | 1089 | u32 rx_flags = COH901318_CX_CTRL_PRDD_DEST | |
1069 | COH901318_CX_CTRL_DST_ADDR_INC_ENABLE; | 1090 | COH901318_CX_CTRL_DST_ADDR_INC_ENABLE; |
1070 | 1091 | ||
@@ -1144,12 +1165,17 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, | |||
1144 | struct dma_tx_state *txstate) | 1165 | struct dma_tx_state *txstate) |
1145 | { | 1166 | { |
1146 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | 1167 | struct coh901318_chan *cohc = to_coh901318_chan(chan); |
1147 | enum dma_status ret; | 1168 | dma_cookie_t last_used; |
1169 | dma_cookie_t last_complete; | ||
1170 | int ret; | ||
1148 | 1171 | ||
1149 | ret = dma_cookie_status(chan, cookie, txstate); | 1172 | last_complete = cohc->completed; |
1150 | /* FIXME: should be conditional on ret != DMA_SUCCESS? */ | 1173 | last_used = chan->cookie; |
1151 | dma_set_residue(txstate, coh901318_get_bytes_left(chan)); | ||
1152 | 1174 | ||
1175 | ret = dma_async_is_complete(cookie, last_complete, last_used); | ||
1176 | |||
1177 | dma_set_tx_state(txstate, last_complete, last_used, | ||
1178 | coh901318_get_bytes_left(chan)); | ||
1153 | if (ret == DMA_IN_PROGRESS && cohc->stopped) | 1179 | if (ret == DMA_IN_PROGRESS && cohc->stopped) |
1154 | ret = DMA_PAUSED; | 1180 | ret = DMA_PAUSED; |
1155 | 1181 | ||
@@ -1248,11 +1274,11 @@ static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, | |||
1248 | int i = 0; | 1274 | int i = 0; |
1249 | 1275 | ||
1250 | /* We only support mem to per or per to mem transfers */ | 1276 | /* We only support mem to per or per to mem transfers */ |
1251 | if (config->direction == DMA_DEV_TO_MEM) { | 1277 | if (config->direction == DMA_FROM_DEVICE) { |
1252 | addr = config->src_addr; | 1278 | addr = config->src_addr; |
1253 | addr_width = config->src_addr_width; | 1279 | addr_width = config->src_addr_width; |
1254 | maxburst = config->src_maxburst; | 1280 | maxburst = config->src_maxburst; |
1255 | } else if (config->direction == DMA_MEM_TO_DEV) { | 1281 | } else if (config->direction == DMA_TO_DEVICE) { |
1256 | addr = config->dst_addr; | 1282 | addr = config->dst_addr; |
1257 | addr_width = config->dst_addr_width; | 1283 | addr_width = config->dst_addr_width; |
1258 | maxburst = config->dst_maxburst; | 1284 | maxburst = config->dst_maxburst; |
@@ -1438,32 +1464,34 @@ static int __init coh901318_probe(struct platform_device *pdev) | |||
1438 | 1464 | ||
1439 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1465 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1440 | if (!io) | 1466 | if (!io) |
1441 | return -ENODEV; | 1467 | goto err_get_resource; |
1442 | 1468 | ||
1443 | /* Map DMA controller registers to virtual memory */ | 1469 | /* Map DMA controller registers to virtual memory */ |
1444 | if (devm_request_mem_region(&pdev->dev, | 1470 | if (request_mem_region(io->start, |
1445 | io->start, | 1471 | resource_size(io), |
1446 | resource_size(io), | 1472 | pdev->dev.driver->name) == NULL) { |
1447 | pdev->dev.driver->name) == NULL) | 1473 | err = -EBUSY; |
1448 | return -ENOMEM; | 1474 | goto err_request_mem; |
1475 | } | ||
1449 | 1476 | ||
1450 | pdata = pdev->dev.platform_data; | 1477 | pdata = pdev->dev.platform_data; |
1451 | if (!pdata) | 1478 | if (!pdata) |
1452 | return -ENODEV; | 1479 | goto err_no_platformdata; |
1453 | 1480 | ||
1454 | base = devm_kzalloc(&pdev->dev, | 1481 | base = kmalloc(ALIGN(sizeof(struct coh901318_base), 4) + |
1455 | ALIGN(sizeof(struct coh901318_base), 4) + | 1482 | pdata->max_channels * |
1456 | pdata->max_channels * | 1483 | sizeof(struct coh901318_chan), |
1457 | sizeof(struct coh901318_chan), | 1484 | GFP_KERNEL); |
1458 | GFP_KERNEL); | ||
1459 | if (!base) | 1485 | if (!base) |
1460 | return -ENOMEM; | 1486 | goto err_alloc_coh_dma_channels; |
1461 | 1487 | ||
1462 | base->chans = ((void *)base) + ALIGN(sizeof(struct coh901318_base), 4); | 1488 | base->chans = ((void *)base) + ALIGN(sizeof(struct coh901318_base), 4); |
1463 | 1489 | ||
1464 | base->virtbase = devm_ioremap(&pdev->dev, io->start, resource_size(io)); | 1490 | base->virtbase = ioremap(io->start, resource_size(io)); |
1465 | if (!base->virtbase) | 1491 | if (!base->virtbase) { |
1466 | return -ENOMEM; | 1492 | err = -ENOMEM; |
1493 | goto err_no_ioremap; | ||
1494 | } | ||
1467 | 1495 | ||
1468 | base->dev = &pdev->dev; | 1496 | base->dev = &pdev->dev; |
1469 | base->platform = pdata; | 1497 | base->platform = pdata; |
@@ -1472,20 +1500,25 @@ static int __init coh901318_probe(struct platform_device *pdev) | |||
1472 | 1500 | ||
1473 | COH901318_DEBUGFS_ASSIGN(debugfs_dma_base, base); | 1501 | COH901318_DEBUGFS_ASSIGN(debugfs_dma_base, base); |
1474 | 1502 | ||
1503 | platform_set_drvdata(pdev, base); | ||
1504 | |||
1475 | irq = platform_get_irq(pdev, 0); | 1505 | irq = platform_get_irq(pdev, 0); |
1476 | if (irq < 0) | 1506 | if (irq < 0) |
1477 | return irq; | 1507 | goto err_no_irq; |
1478 | 1508 | ||
1479 | err = devm_request_irq(&pdev->dev, irq, dma_irq_handler, IRQF_DISABLED, | 1509 | err = request_irq(irq, dma_irq_handler, IRQF_DISABLED, |
1480 | "coh901318", base); | 1510 | "coh901318", base); |
1481 | if (err) | 1511 | if (err) { |
1482 | return err; | 1512 | dev_crit(&pdev->dev, |
1513 | "Cannot allocate IRQ for DMA controller!\n"); | ||
1514 | goto err_request_irq; | ||
1515 | } | ||
1483 | 1516 | ||
1484 | err = coh901318_pool_create(&base->pool, &pdev->dev, | 1517 | err = coh901318_pool_create(&base->pool, &pdev->dev, |
1485 | sizeof(struct coh901318_lli), | 1518 | sizeof(struct coh901318_lli), |
1486 | 32); | 1519 | 32); |
1487 | if (err) | 1520 | if (err) |
1488 | return err; | 1521 | goto err_pool_create; |
1489 | 1522 | ||
1490 | /* init channels for device transfers */ | 1523 | /* init channels for device transfers */ |
1491 | coh901318_base_init(&base->dma_slave, base->platform->chans_slave, | 1524 | coh901318_base_init(&base->dma_slave, base->platform->chans_slave, |
@@ -1531,7 +1564,6 @@ static int __init coh901318_probe(struct platform_device *pdev) | |||
1531 | if (err) | 1564 | if (err) |
1532 | goto err_register_memcpy; | 1565 | goto err_register_memcpy; |
1533 | 1566 | ||
1534 | platform_set_drvdata(pdev, base); | ||
1535 | dev_info(&pdev->dev, "Initialized COH901318 DMA on virtual base 0x%08x\n", | 1567 | dev_info(&pdev->dev, "Initialized COH901318 DMA on virtual base 0x%08x\n", |
1536 | (u32) base->virtbase); | 1568 | (u32) base->virtbase); |
1537 | 1569 | ||
@@ -1541,6 +1573,19 @@ static int __init coh901318_probe(struct platform_device *pdev) | |||
1541 | dma_async_device_unregister(&base->dma_slave); | 1573 | dma_async_device_unregister(&base->dma_slave); |
1542 | err_register_slave: | 1574 | err_register_slave: |
1543 | coh901318_pool_destroy(&base->pool); | 1575 | coh901318_pool_destroy(&base->pool); |
1576 | err_pool_create: | ||
1577 | free_irq(platform_get_irq(pdev, 0), base); | ||
1578 | err_request_irq: | ||
1579 | err_no_irq: | ||
1580 | iounmap(base->virtbase); | ||
1581 | err_no_ioremap: | ||
1582 | kfree(base); | ||
1583 | err_alloc_coh_dma_channels: | ||
1584 | err_no_platformdata: | ||
1585 | release_mem_region(pdev->resource->start, | ||
1586 | resource_size(pdev->resource)); | ||
1587 | err_request_mem: | ||
1588 | err_get_resource: | ||
1544 | return err; | 1589 | return err; |
1545 | } | 1590 | } |
1546 | 1591 | ||
@@ -1551,6 +1596,11 @@ static int __exit coh901318_remove(struct platform_device *pdev) | |||
1551 | dma_async_device_unregister(&base->dma_memcpy); | 1596 | dma_async_device_unregister(&base->dma_memcpy); |
1552 | dma_async_device_unregister(&base->dma_slave); | 1597 | dma_async_device_unregister(&base->dma_slave); |
1553 | coh901318_pool_destroy(&base->pool); | 1598 | coh901318_pool_destroy(&base->pool); |
1599 | free_irq(platform_get_irq(pdev, 0), base); | ||
1600 | iounmap(base->virtbase); | ||
1601 | kfree(base); | ||
1602 | release_mem_region(pdev->resource->start, | ||
1603 | resource_size(pdev->resource)); | ||
1554 | return 0; | 1604 | return 0; |
1555 | } | 1605 | } |
1556 | 1606 | ||