diff options
| -rw-r--r-- | drivers/dma/amba-pl08x.c | 268 |
1 files changed, 112 insertions, 156 deletions
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 30b6921f094f..bbae30ceb8fb 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c | |||
| @@ -210,8 +210,6 @@ enum pl08x_dma_chan_state { | |||
| 210 | * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel | 210 | * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel |
| 211 | * @chan: wrappped abstract channel | 211 | * @chan: wrappped abstract channel |
| 212 | * @phychan: the physical channel utilized by this channel, if there is one | 212 | * @phychan: the physical channel utilized by this channel, if there is one |
| 213 | * @phychan_hold: if non-zero, hold on to the physical channel even if we | ||
| 214 | * have no pending entries | ||
| 215 | * @tasklet: tasklet scheduled by the IRQ to handle actual work etc | 213 | * @tasklet: tasklet scheduled by the IRQ to handle actual work etc |
| 216 | * @name: name of channel | 214 | * @name: name of channel |
| 217 | * @cd: channel platform data | 215 | * @cd: channel platform data |
| @@ -230,7 +228,6 @@ enum pl08x_dma_chan_state { | |||
| 230 | struct pl08x_dma_chan { | 228 | struct pl08x_dma_chan { |
| 231 | struct dma_chan chan; | 229 | struct dma_chan chan; |
| 232 | struct pl08x_phy_chan *phychan; | 230 | struct pl08x_phy_chan *phychan; |
| 233 | int phychan_hold; | ||
| 234 | struct tasklet_struct tasklet; | 231 | struct tasklet_struct tasklet; |
| 235 | const char *name; | 232 | const char *name; |
| 236 | const struct pl08x_channel_data *cd; | 233 | const struct pl08x_channel_data *cd; |
| @@ -587,19 +584,111 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, | |||
| 587 | return ch; | 584 | return ch; |
| 588 | } | 585 | } |
| 589 | 586 | ||
| 587 | /* Mark the physical channel as free. Note, this write is atomic. */ | ||
| 590 | static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, | 588 | static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, |
| 591 | struct pl08x_phy_chan *ch) | 589 | struct pl08x_phy_chan *ch) |
| 592 | { | 590 | { |
| 593 | unsigned long flags; | 591 | ch->serving = NULL; |
| 592 | } | ||
| 594 | 593 | ||
| 595 | spin_lock_irqsave(&ch->lock, flags); | 594 | /* |
| 595 | * Try to allocate a physical channel. When successful, assign it to | ||
| 596 | * this virtual channel, and initiate the next descriptor. The | ||
| 597 | * virtual channel lock must be held at this point. | ||
| 598 | */ | ||
| 599 | static void pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan) | ||
| 600 | { | ||
| 601 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 602 | struct pl08x_phy_chan *ch; | ||
| 596 | 603 | ||
| 597 | /* Stop the channel and clear its interrupts */ | 604 | ch = pl08x_get_phy_channel(pl08x, plchan); |
| 598 | pl08x_terminate_phy_chan(pl08x, ch); | 605 | if (!ch) { |
| 606 | dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name); | ||
| 607 | plchan->state = PL08X_CHAN_WAITING; | ||
| 608 | return; | ||
| 609 | } | ||
| 599 | 610 | ||
| 600 | /* Mark it as free */ | 611 | dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n", |
| 601 | ch->serving = NULL; | 612 | ch->id, plchan->name); |
| 602 | spin_unlock_irqrestore(&ch->lock, flags); | 613 | |
| 614 | plchan->phychan = ch; | ||
| 615 | plchan->state = PL08X_CHAN_RUNNING; | ||
| 616 | pl08x_start_next_txd(plchan); | ||
| 617 | } | ||
| 618 | |||
| 619 | static void pl08x_phy_reassign_start(struct pl08x_phy_chan *ch, | ||
| 620 | struct pl08x_dma_chan *plchan) | ||
| 621 | { | ||
| 622 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 623 | |||
| 624 | dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n", | ||
| 625 | ch->id, plchan->name); | ||
| 626 | |||
| 627 | /* | ||
| 628 | * We do this without taking the lock; we're really only concerned | ||
| 629 | * about whether this pointer is NULL or not, and we're guaranteed | ||
| 630 | * that this will only be called when it _already_ is non-NULL. | ||
| 631 | */ | ||
| 632 | ch->serving = plchan; | ||
| 633 | plchan->phychan = ch; | ||
| 634 | plchan->state = PL08X_CHAN_RUNNING; | ||
| 635 | pl08x_start_next_txd(plchan); | ||
| 636 | } | ||
| 637 | |||
| 638 | /* | ||
| 639 | * Free a physical DMA channel, potentially reallocating it to another | ||
| 640 | * virtual channel if we have any pending. | ||
| 641 | */ | ||
| 642 | static void pl08x_phy_free(struct pl08x_dma_chan *plchan) | ||
| 643 | { | ||
| 644 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 645 | struct pl08x_dma_chan *p, *next; | ||
| 646 | |||
| 647 | retry: | ||
| 648 | next = NULL; | ||
| 649 | |||
| 650 | /* Find a waiting virtual channel for the next transfer. */ | ||
| 651 | list_for_each_entry(p, &pl08x->memcpy.channels, chan.device_node) | ||
| 652 | if (p->state == PL08X_CHAN_WAITING) { | ||
| 653 | next = p; | ||
| 654 | break; | ||
| 655 | } | ||
| 656 | |||
| 657 | if (!next) { | ||
| 658 | list_for_each_entry(p, &pl08x->slave.channels, chan.device_node) | ||
| 659 | if (p->state == PL08X_CHAN_WAITING) { | ||
| 660 | next = p; | ||
| 661 | break; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | /* Ensure that the physical channel is stopped */ | ||
| 666 | pl08x_terminate_phy_chan(pl08x, plchan->phychan); | ||
| 667 | |||
| 668 | if (next) { | ||
| 669 | bool success; | ||
| 670 | |||
| 671 | /* | ||
| 672 | * Eww. We know this isn't going to deadlock | ||
| 673 | * but lockdep probably doesn't. | ||
| 674 | */ | ||
| 675 | spin_lock(&next->lock); | ||
| 676 | /* Re-check the state now that we have the lock */ | ||
| 677 | success = next->state == PL08X_CHAN_WAITING; | ||
| 678 | if (success) | ||
| 679 | pl08x_phy_reassign_start(plchan->phychan, next); | ||
| 680 | spin_unlock(&next->lock); | ||
| 681 | |||
| 682 | /* If the state changed, try to find another channel */ | ||
| 683 | if (!success) | ||
| 684 | goto retry; | ||
| 685 | } else { | ||
| 686 | /* No more jobs, so free up the physical channel */ | ||
| 687 | pl08x_put_phy_channel(pl08x, plchan->phychan); | ||
| 688 | } | ||
| 689 | |||
| 690 | plchan->phychan = NULL; | ||
| 691 | plchan->state = PL08X_CHAN_IDLE; | ||
| 603 | } | 692 | } |
| 604 | 693 | ||
| 605 | /* | 694 | /* |
| @@ -1028,45 +1117,6 @@ static void pl08x_free_chan_resources(struct dma_chan *chan) | |||
| 1028 | { | 1117 | { |
| 1029 | } | 1118 | } |
| 1030 | 1119 | ||
| 1031 | /* | ||
| 1032 | * This should be called with the channel plchan->lock held | ||
| 1033 | */ | ||
| 1034 | static int prep_phy_channel(struct pl08x_dma_chan *plchan) | ||
| 1035 | { | ||
| 1036 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 1037 | struct pl08x_phy_chan *ch; | ||
| 1038 | |||
| 1039 | /* Check if we already have a channel */ | ||
| 1040 | if (plchan->phychan) { | ||
| 1041 | ch = plchan->phychan; | ||
| 1042 | goto got_channel; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | ch = pl08x_get_phy_channel(pl08x, plchan); | ||
| 1046 | if (!ch) { | ||
| 1047 | /* No physical channel available, cope with it */ | ||
| 1048 | dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name); | ||
| 1049 | return -EBUSY; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | plchan->phychan = ch; | ||
| 1053 | dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n", | ||
| 1054 | ch->id, plchan->name); | ||
| 1055 | |||
| 1056 | got_channel: | ||
| 1057 | plchan->phychan_hold++; | ||
| 1058 | |||
| 1059 | return 0; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | static void release_phy_channel(struct pl08x_dma_chan *plchan) | ||
| 1063 | { | ||
| 1064 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 1065 | |||
| 1066 | pl08x_put_phy_channel(pl08x, plchan->phychan); | ||
| 1067 | plchan->phychan = NULL; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx) | 1120 | static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx) |
| 1071 | { | 1121 | { |
| 1072 | struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan); | 1122 | struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan); |
| @@ -1079,19 +1129,6 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx) | |||
| 1079 | 1129 | ||
| 1080 | /* Put this onto the pending list */ | 1130 | /* Put this onto the pending list */ |
| 1081 | list_add_tail(&txd->node, &plchan->pend_list); | 1131 | list_add_tail(&txd->node, &plchan->pend_list); |
| 1082 | |||
| 1083 | /* | ||
| 1084 | * If there was no physical channel available for this memcpy, | ||
| 1085 | * stack the request up and indicate that the channel is waiting | ||
| 1086 | * for a free physical channel. | ||
| 1087 | */ | ||
| 1088 | if (!plchan->slave && !plchan->phychan) { | ||
| 1089 | /* Do this memcpy whenever there is a channel ready */ | ||
| 1090 | plchan->state = PL08X_CHAN_WAITING; | ||
| 1091 | } else { | ||
| 1092 | plchan->phychan_hold--; | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | spin_unlock_irqrestore(&plchan->lock, flags); | 1132 | spin_unlock_irqrestore(&plchan->lock, flags); |
| 1096 | 1133 | ||
| 1097 | return cookie; | 1134 | return cookie; |
| @@ -1282,19 +1319,10 @@ static void pl08x_issue_pending(struct dma_chan *chan) | |||
| 1282 | 1319 | ||
| 1283 | spin_lock_irqsave(&plchan->lock, flags); | 1320 | spin_lock_irqsave(&plchan->lock, flags); |
| 1284 | list_splice_tail_init(&plchan->pend_list, &plchan->issued_list); | 1321 | list_splice_tail_init(&plchan->pend_list, &plchan->issued_list); |
| 1285 | |||
| 1286 | /* Something is already active, or we're waiting for a channel... */ | ||
| 1287 | if (plchan->at || plchan->state == PL08X_CHAN_WAITING) { | ||
| 1288 | spin_unlock_irqrestore(&plchan->lock, flags); | ||
| 1289 | return; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | /* Take the first element in the queue and execute it */ | ||
| 1293 | if (!list_empty(&plchan->issued_list)) { | 1322 | if (!list_empty(&plchan->issued_list)) { |
| 1294 | plchan->state = PL08X_CHAN_RUNNING; | 1323 | if (!plchan->phychan && plchan->state != PL08X_CHAN_WAITING) |
| 1295 | pl08x_start_next_txd(plchan); | 1324 | pl08x_phy_alloc_and_start(plchan); |
| 1296 | } | 1325 | } |
| 1297 | |||
| 1298 | spin_unlock_irqrestore(&plchan->lock, flags); | 1326 | spin_unlock_irqrestore(&plchan->lock, flags); |
| 1299 | } | 1327 | } |
| 1300 | 1328 | ||
| @@ -1302,48 +1330,18 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, | |||
| 1302 | struct pl08x_txd *txd) | 1330 | struct pl08x_txd *txd) |
| 1303 | { | 1331 | { |
| 1304 | struct pl08x_driver_data *pl08x = plchan->host; | 1332 | struct pl08x_driver_data *pl08x = plchan->host; |
| 1305 | unsigned long flags; | 1333 | int num_llis; |
| 1306 | int num_llis, ret; | ||
| 1307 | 1334 | ||
| 1308 | num_llis = pl08x_fill_llis_for_desc(pl08x, txd); | 1335 | num_llis = pl08x_fill_llis_for_desc(pl08x, txd); |
| 1309 | if (!num_llis) { | 1336 | if (!num_llis) { |
| 1337 | unsigned long flags; | ||
| 1338 | |||
| 1310 | spin_lock_irqsave(&plchan->lock, flags); | 1339 | spin_lock_irqsave(&plchan->lock, flags); |
| 1311 | pl08x_free_txd(pl08x, txd); | 1340 | pl08x_free_txd(pl08x, txd); |
| 1312 | spin_unlock_irqrestore(&plchan->lock, flags); | 1341 | spin_unlock_irqrestore(&plchan->lock, flags); |
| 1342 | |||
| 1313 | return -EINVAL; | 1343 | return -EINVAL; |
| 1314 | } | 1344 | } |
| 1315 | |||
| 1316 | spin_lock_irqsave(&plchan->lock, flags); | ||
| 1317 | |||
| 1318 | /* | ||
| 1319 | * See if we already have a physical channel allocated, | ||
| 1320 | * else this is the time to try to get one. | ||
| 1321 | */ | ||
| 1322 | ret = prep_phy_channel(plchan); | ||
| 1323 | if (ret) { | ||
| 1324 | /* | ||
| 1325 | * No physical channel was available. | ||
| 1326 | * | ||
| 1327 | * memcpy transfers can be sorted out at submission time. | ||
| 1328 | */ | ||
| 1329 | if (plchan->slave) { | ||
| 1330 | pl08x_free_txd_list(pl08x, plchan); | ||
| 1331 | pl08x_free_txd(pl08x, txd); | ||
| 1332 | spin_unlock_irqrestore(&plchan->lock, flags); | ||
| 1333 | return -EBUSY; | ||
| 1334 | } | ||
| 1335 | } else | ||
| 1336 | /* | ||
| 1337 | * Else we're all set, paused and ready to roll, status | ||
| 1338 | * will switch to PL08X_CHAN_RUNNING when we call | ||
| 1339 | * issue_pending(). If there is something running on the | ||
| 1340 | * channel already we don't change its state. | ||
| 1341 | */ | ||
| 1342 | if (plchan->state == PL08X_CHAN_IDLE) | ||
| 1343 | plchan->state = PL08X_CHAN_PAUSED; | ||
| 1344 | |||
| 1345 | spin_unlock_irqrestore(&plchan->lock, flags); | ||
| 1346 | |||
| 1347 | return 0; | 1345 | return 0; |
| 1348 | } | 1346 | } |
| 1349 | 1347 | ||
| @@ -1563,14 +1561,11 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
| 1563 | plchan->state = PL08X_CHAN_IDLE; | 1561 | plchan->state = PL08X_CHAN_IDLE; |
| 1564 | 1562 | ||
| 1565 | if (plchan->phychan) { | 1563 | if (plchan->phychan) { |
| 1566 | pl08x_terminate_phy_chan(pl08x, plchan->phychan); | ||
| 1567 | |||
| 1568 | /* | 1564 | /* |
| 1569 | * Mark physical channel as free and free any slave | 1565 | * Mark physical channel as free and free any slave |
| 1570 | * signal | 1566 | * signal |
| 1571 | */ | 1567 | */ |
| 1572 | release_phy_channel(plchan); | 1568 | pl08x_phy_free(plchan); |
| 1573 | plchan->phychan_hold = 0; | ||
| 1574 | } | 1569 | } |
| 1575 | /* Dequeue jobs and free LLIs */ | 1570 | /* Dequeue jobs and free LLIs */ |
| 1576 | if (plchan->at) { | 1571 | if (plchan->at) { |
| @@ -1670,50 +1665,6 @@ static void pl08x_tasklet(unsigned long data) | |||
| 1670 | 1665 | ||
| 1671 | spin_lock_irqsave(&plchan->lock, flags); | 1666 | spin_lock_irqsave(&plchan->lock, flags); |
| 1672 | list_splice_tail_init(&plchan->done_list, &head); | 1667 | list_splice_tail_init(&plchan->done_list, &head); |
| 1673 | |||
| 1674 | if (plchan->at || !list_empty(&plchan->pend_list) || plchan->phychan_hold) { | ||
| 1675 | /* | ||
| 1676 | * This channel is still in use - we have a new txd being | ||
| 1677 | * prepared and will soon be queued. Don't give up the | ||
| 1678 | * physical channel. | ||
| 1679 | */ | ||
| 1680 | } else { | ||
| 1681 | struct pl08x_dma_chan *waiting = NULL; | ||
| 1682 | |||
| 1683 | /* | ||
| 1684 | * No more jobs, so free up the physical channel | ||
| 1685 | */ | ||
| 1686 | release_phy_channel(plchan); | ||
| 1687 | plchan->state = PL08X_CHAN_IDLE; | ||
| 1688 | |||
| 1689 | /* | ||
| 1690 | * And NOW before anyone else can grab that free:d up | ||
| 1691 | * physical channel, see if there is some memcpy pending | ||
| 1692 | * that seriously needs to start because of being stacked | ||
| 1693 | * up while we were choking the physical channels with data. | ||
| 1694 | */ | ||
| 1695 | list_for_each_entry(waiting, &pl08x->memcpy.channels, | ||
| 1696 | chan.device_node) { | ||
| 1697 | if (waiting->state == PL08X_CHAN_WAITING) { | ||
| 1698 | int ret; | ||
| 1699 | |||
| 1700 | /* This should REALLY not fail now */ | ||
| 1701 | ret = prep_phy_channel(waiting); | ||
| 1702 | BUG_ON(ret); | ||
| 1703 | waiting->phychan_hold--; | ||
| 1704 | waiting->state = PL08X_CHAN_RUNNING; | ||
| 1705 | /* | ||
| 1706 | * Eww. We know this isn't going to deadlock | ||
| 1707 | * but lockdep probably doens't. | ||
| 1708 | */ | ||
| 1709 | spin_lock(&waiting->lock); | ||
| 1710 | pl08x_start_next_txd(waiting); | ||
| 1711 | spin_unlock(&waiting->lock); | ||
| 1712 | break; | ||
| 1713 | } | ||
| 1714 | } | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | spin_unlock_irqrestore(&plchan->lock, flags); | 1668 | spin_unlock_irqrestore(&plchan->lock, flags); |
| 1718 | 1669 | ||
| 1719 | while (!list_empty(&head)) { | 1670 | while (!list_empty(&head)) { |
| @@ -1784,9 +1735,14 @@ static irqreturn_t pl08x_irq(int irq, void *dev) | |||
| 1784 | dma_cookie_complete(&tx->tx); | 1735 | dma_cookie_complete(&tx->tx); |
| 1785 | list_add_tail(&tx->node, &plchan->done_list); | 1736 | list_add_tail(&tx->node, &plchan->done_list); |
| 1786 | 1737 | ||
| 1787 | /* And start the next descriptor */ | 1738 | /* |
| 1739 | * And start the next descriptor (if any), | ||
| 1740 | * otherwise free this channel. | ||
| 1741 | */ | ||
| 1788 | if (!list_empty(&plchan->issued_list)) | 1742 | if (!list_empty(&plchan->issued_list)) |
| 1789 | pl08x_start_next_txd(plchan); | 1743 | pl08x_start_next_txd(plchan); |
| 1744 | else | ||
| 1745 | pl08x_phy_free(plchan); | ||
| 1790 | } | 1746 | } |
| 1791 | spin_unlock(&plchan->lock); | 1747 | spin_unlock(&plchan->lock); |
| 1792 | 1748 | ||
