diff options
Diffstat (limited to 'arch/arm/common/pl330.c')
-rw-r--r-- | arch/arm/common/pl330.c | 124 |
1 files changed, 56 insertions, 68 deletions
diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index f407a6b35d3d..d8e44a43047c 100644 --- a/arch/arm/common/pl330.c +++ b/arch/arm/common/pl330.c | |||
@@ -221,17 +221,6 @@ | |||
221 | */ | 221 | */ |
222 | #define MCODE_BUFF_PER_REQ 256 | 222 | #define MCODE_BUFF_PER_REQ 256 |
223 | 223 | ||
224 | /* | ||
225 | * Mark a _pl330_req as free. | ||
226 | * We do it by writing DMAEND as the first instruction | ||
227 | * because no valid request is going to have DMAEND as | ||
228 | * its first instruction to execute. | ||
229 | */ | ||
230 | #define MARK_FREE(req) do { \ | ||
231 | _emit_END(0, (req)->mc_cpu); \ | ||
232 | (req)->mc_len = 0; \ | ||
233 | } while (0) | ||
234 | |||
235 | /* If the _pl330_req is available to the client */ | 224 | /* If the _pl330_req is available to the client */ |
236 | #define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND) | 225 | #define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND) |
237 | 226 | ||
@@ -301,8 +290,10 @@ struct pl330_thread { | |||
301 | struct pl330_dmac *dmac; | 290 | struct pl330_dmac *dmac; |
302 | /* Only two at a time */ | 291 | /* Only two at a time */ |
303 | struct _pl330_req req[2]; | 292 | struct _pl330_req req[2]; |
304 | /* Index of the last submitted request */ | 293 | /* Index of the last enqueued request */ |
305 | unsigned lstenq; | 294 | unsigned lstenq; |
295 | /* Index of the last submitted request or -1 if the DMA is stopped */ | ||
296 | int req_running; | ||
306 | }; | 297 | }; |
307 | 298 | ||
308 | enum pl330_dmac_state { | 299 | enum pl330_dmac_state { |
@@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd, | |||
778 | writel(0, regs + DBGCMD); | 769 | writel(0, regs + DBGCMD); |
779 | } | 770 | } |
780 | 771 | ||
772 | /* | ||
773 | * Mark a _pl330_req as free. | ||
774 | * We do it by writing DMAEND as the first instruction | ||
775 | * because no valid request is going to have DMAEND as | ||
776 | * its first instruction to execute. | ||
777 | */ | ||
778 | static void mark_free(struct pl330_thread *thrd, int idx) | ||
779 | { | ||
780 | struct _pl330_req *req = &thrd->req[idx]; | ||
781 | |||
782 | _emit_END(0, req->mc_cpu); | ||
783 | req->mc_len = 0; | ||
784 | |||
785 | thrd->req_running = -1; | ||
786 | } | ||
787 | |||
781 | static inline u32 _state(struct pl330_thread *thrd) | 788 | static inline u32 _state(struct pl330_thread *thrd) |
782 | { | 789 | { |
783 | void __iomem *regs = thrd->dmac->pinfo->base; | 790 | void __iomem *regs = thrd->dmac->pinfo->base; |
@@ -836,31 +843,6 @@ static inline u32 _state(struct pl330_thread *thrd) | |||
836 | } | 843 | } |
837 | } | 844 | } |
838 | 845 | ||
839 | /* If the request 'req' of thread 'thrd' is currently active */ | ||
840 | static inline bool _req_active(struct pl330_thread *thrd, | ||
841 | struct _pl330_req *req) | ||
842 | { | ||
843 | void __iomem *regs = thrd->dmac->pinfo->base; | ||
844 | u32 buf = req->mc_bus, pc = readl(regs + CPC(thrd->id)); | ||
845 | |||
846 | if (IS_FREE(req)) | ||
847 | return false; | ||
848 | |||
849 | return (pc >= buf && pc <= buf + req->mc_len) ? true : false; | ||
850 | } | ||
851 | |||
852 | /* Returns 0 if the thread is inactive, ID of active req + 1 otherwise */ | ||
853 | static inline unsigned _thrd_active(struct pl330_thread *thrd) | ||
854 | { | ||
855 | if (_req_active(thrd, &thrd->req[0])) | ||
856 | return 1; /* First req active */ | ||
857 | |||
858 | if (_req_active(thrd, &thrd->req[1])) | ||
859 | return 2; /* Second req active */ | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static void _stop(struct pl330_thread *thrd) | 846 | static void _stop(struct pl330_thread *thrd) |
865 | { | 847 | { |
866 | void __iomem *regs = thrd->dmac->pinfo->base; | 848 | void __iomem *regs = thrd->dmac->pinfo->base; |
@@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd) | |||
892 | struct _arg_GO go; | 874 | struct _arg_GO go; |
893 | unsigned ns; | 875 | unsigned ns; |
894 | u8 insn[6] = {0, 0, 0, 0, 0, 0}; | 876 | u8 insn[6] = {0, 0, 0, 0, 0, 0}; |
877 | int idx; | ||
895 | 878 | ||
896 | /* Return if already ACTIVE */ | 879 | /* Return if already ACTIVE */ |
897 | if (_state(thrd) != PL330_STATE_STOPPED) | 880 | if (_state(thrd) != PL330_STATE_STOPPED) |
898 | return true; | 881 | return true; |
899 | 882 | ||
900 | if (!IS_FREE(&thrd->req[1 - thrd->lstenq])) | 883 | idx = 1 - thrd->lstenq; |
901 | req = &thrd->req[1 - thrd->lstenq]; | 884 | if (!IS_FREE(&thrd->req[idx])) |
902 | else if (!IS_FREE(&thrd->req[thrd->lstenq])) | 885 | req = &thrd->req[idx]; |
903 | req = &thrd->req[thrd->lstenq]; | 886 | else { |
904 | else | 887 | idx = thrd->lstenq; |
905 | req = NULL; | 888 | if (!IS_FREE(&thrd->req[idx])) |
889 | req = &thrd->req[idx]; | ||
890 | else | ||
891 | req = NULL; | ||
892 | } | ||
906 | 893 | ||
907 | /* Return if no request */ | 894 | /* Return if no request */ |
908 | if (!req || !req->r) | 895 | if (!req || !req->r) |
@@ -933,6 +920,8 @@ static bool _trigger(struct pl330_thread *thrd) | |||
933 | /* Only manager can execute GO */ | 920 | /* Only manager can execute GO */ |
934 | _execute_DBGINSN(thrd, insn, true); | 921 | _execute_DBGINSN(thrd, insn, true); |
935 | 922 | ||
923 | thrd->req_running = idx; | ||
924 | |||
936 | return true; | 925 | return true; |
937 | } | 926 | } |
938 | 927 | ||
@@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data) | |||
1382 | 1371 | ||
1383 | thrd->req[0].r = NULL; | 1372 | thrd->req[0].r = NULL; |
1384 | thrd->req[1].r = NULL; | 1373 | thrd->req[1].r = NULL; |
1385 | MARK_FREE(&thrd->req[0]); | 1374 | mark_free(thrd, 0); |
1386 | MARK_FREE(&thrd->req[1]); | 1375 | mark_free(thrd, 1); |
1387 | 1376 | ||
1388 | /* Clear the reset flag */ | 1377 | /* Clear the reset flag */ |
1389 | pl330->dmac_tbd.reset_chan &= ~(1 << i); | 1378 | pl330->dmac_tbd.reset_chan &= ~(1 << i); |
@@ -1461,14 +1450,12 @@ int pl330_update(const struct pl330_info *pi) | |||
1461 | 1450 | ||
1462 | thrd = &pl330->channels[id]; | 1451 | thrd = &pl330->channels[id]; |
1463 | 1452 | ||
1464 | active = _thrd_active(thrd); | 1453 | active = thrd->req_running; |
1465 | if (!active) /* Aborted */ | 1454 | if (active == -1) /* Aborted */ |
1466 | continue; | 1455 | continue; |
1467 | 1456 | ||
1468 | active -= 1; | ||
1469 | |||
1470 | rqdone = &thrd->req[active]; | 1457 | rqdone = &thrd->req[active]; |
1471 | MARK_FREE(rqdone); | 1458 | mark_free(thrd, active); |
1472 | 1459 | ||
1473 | /* Get going again ASAP */ | 1460 | /* Get going again ASAP */ |
1474 | _start(thrd); | 1461 | _start(thrd); |
@@ -1480,13 +1467,19 @@ int pl330_update(const struct pl330_info *pi) | |||
1480 | 1467 | ||
1481 | /* Now that we are in no hurry, do the callbacks */ | 1468 | /* Now that we are in no hurry, do the callbacks */ |
1482 | while (!list_empty(&pl330->req_done)) { | 1469 | while (!list_empty(&pl330->req_done)) { |
1470 | struct pl330_req *r; | ||
1471 | |||
1483 | rqdone = container_of(pl330->req_done.next, | 1472 | rqdone = container_of(pl330->req_done.next, |
1484 | struct _pl330_req, rqd); | 1473 | struct _pl330_req, rqd); |
1485 | 1474 | ||
1486 | list_del_init(&rqdone->rqd); | 1475 | list_del_init(&rqdone->rqd); |
1487 | 1476 | ||
1477 | /* Detach the req */ | ||
1478 | r = rqdone->r; | ||
1479 | rqdone->r = NULL; | ||
1480 | |||
1488 | spin_unlock_irqrestore(&pl330->lock, flags); | 1481 | spin_unlock_irqrestore(&pl330->lock, flags); |
1489 | _callback(rqdone->r, PL330_ERR_NONE); | 1482 | _callback(r, PL330_ERR_NONE); |
1490 | spin_lock_irqsave(&pl330->lock, flags); | 1483 | spin_lock_irqsave(&pl330->lock, flags); |
1491 | } | 1484 | } |
1492 | 1485 | ||
@@ -1509,7 +1502,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) | |||
1509 | struct pl330_thread *thrd = ch_id; | 1502 | struct pl330_thread *thrd = ch_id; |
1510 | struct pl330_dmac *pl330; | 1503 | struct pl330_dmac *pl330; |
1511 | unsigned long flags; | 1504 | unsigned long flags; |
1512 | int ret = 0, active; | 1505 | int ret = 0, active = thrd->req_running; |
1513 | 1506 | ||
1514 | if (!thrd || thrd->free || thrd->dmac->state == DYING) | 1507 | if (!thrd || thrd->free || thrd->dmac->state == DYING) |
1515 | return -EINVAL; | 1508 | return -EINVAL; |
@@ -1525,28 +1518,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) | |||
1525 | 1518 | ||
1526 | thrd->req[0].r = NULL; | 1519 | thrd->req[0].r = NULL; |
1527 | thrd->req[1].r = NULL; | 1520 | thrd->req[1].r = NULL; |
1528 | MARK_FREE(&thrd->req[0]); | 1521 | mark_free(thrd, 0); |
1529 | MARK_FREE(&thrd->req[1]); | 1522 | mark_free(thrd, 1); |
1530 | break; | 1523 | break; |
1531 | 1524 | ||
1532 | case PL330_OP_ABORT: | 1525 | case PL330_OP_ABORT: |
1533 | active = _thrd_active(thrd); | ||
1534 | |||
1535 | /* Make sure the channel is stopped */ | 1526 | /* Make sure the channel is stopped */ |
1536 | _stop(thrd); | 1527 | _stop(thrd); |
1537 | 1528 | ||
1538 | /* ABORT is only for the active req */ | 1529 | /* ABORT is only for the active req */ |
1539 | if (!active) | 1530 | if (active == -1) |
1540 | break; | 1531 | break; |
1541 | 1532 | ||
1542 | active--; | ||
1543 | |||
1544 | thrd->req[active].r = NULL; | 1533 | thrd->req[active].r = NULL; |
1545 | MARK_FREE(&thrd->req[active]); | 1534 | mark_free(thrd, active); |
1546 | 1535 | ||
1547 | /* Start the next */ | 1536 | /* Start the next */ |
1548 | case PL330_OP_START: | 1537 | case PL330_OP_START: |
1549 | if (!_thrd_active(thrd) && !_start(thrd)) | 1538 | if ((active == -1) && !_start(thrd)) |
1550 | ret = -EIO; | 1539 | ret = -EIO; |
1551 | break; | 1540 | break; |
1552 | 1541 | ||
@@ -1587,14 +1576,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) | |||
1587 | else | 1576 | else |
1588 | pstatus->faulting = false; | 1577 | pstatus->faulting = false; |
1589 | 1578 | ||
1590 | active = _thrd_active(thrd); | 1579 | active = thrd->req_running; |
1591 | 1580 | ||
1592 | if (!active) { | 1581 | if (active == -1) { |
1593 | /* Indicate that the thread is not running */ | 1582 | /* Indicate that the thread is not running */ |
1594 | pstatus->top_req = NULL; | 1583 | pstatus->top_req = NULL; |
1595 | pstatus->wait_req = NULL; | 1584 | pstatus->wait_req = NULL; |
1596 | } else { | 1585 | } else { |
1597 | active--; | ||
1598 | pstatus->top_req = thrd->req[active].r; | 1586 | pstatus->top_req = thrd->req[active].r; |
1599 | pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) | 1587 | pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) |
1600 | ? thrd->req[1 - active].r : NULL; | 1588 | ? thrd->req[1 - active].r : NULL; |
@@ -1659,9 +1647,9 @@ void *pl330_request_channel(const struct pl330_info *pi) | |||
1659 | thrd->free = false; | 1647 | thrd->free = false; |
1660 | thrd->lstenq = 1; | 1648 | thrd->lstenq = 1; |
1661 | thrd->req[0].r = NULL; | 1649 | thrd->req[0].r = NULL; |
1662 | MARK_FREE(&thrd->req[0]); | 1650 | mark_free(thrd, 0); |
1663 | thrd->req[1].r = NULL; | 1651 | thrd->req[1].r = NULL; |
1664 | MARK_FREE(&thrd->req[1]); | 1652 | mark_free(thrd, 1); |
1665 | break; | 1653 | break; |
1666 | } | 1654 | } |
1667 | } | 1655 | } |
@@ -1767,14 +1755,14 @@ static inline void _reset_thread(struct pl330_thread *thrd) | |||
1767 | thrd->req[0].mc_bus = pl330->mcode_bus | 1755 | thrd->req[0].mc_bus = pl330->mcode_bus |
1768 | + (thrd->id * pi->mcbufsz); | 1756 | + (thrd->id * pi->mcbufsz); |
1769 | thrd->req[0].r = NULL; | 1757 | thrd->req[0].r = NULL; |
1770 | MARK_FREE(&thrd->req[0]); | 1758 | mark_free(thrd, 0); |
1771 | 1759 | ||
1772 | thrd->req[1].mc_cpu = thrd->req[0].mc_cpu | 1760 | thrd->req[1].mc_cpu = thrd->req[0].mc_cpu |
1773 | + pi->mcbufsz / 2; | 1761 | + pi->mcbufsz / 2; |
1774 | thrd->req[1].mc_bus = thrd->req[0].mc_bus | 1762 | thrd->req[1].mc_bus = thrd->req[0].mc_bus |
1775 | + pi->mcbufsz / 2; | 1763 | + pi->mcbufsz / 2; |
1776 | thrd->req[1].r = NULL; | 1764 | thrd->req[1].r = NULL; |
1777 | MARK_FREE(&thrd->req[1]); | 1765 | mark_free(thrd, 1); |
1778 | } | 1766 | } |
1779 | 1767 | ||
1780 | static int dmac_alloc_threads(struct pl330_dmac *pl330) | 1768 | static int dmac_alloc_threads(struct pl330_dmac *pl330) |