diff options
| -rw-r--r-- | arch/arm/common/pl330.c | 116 |
1 files changed, 49 insertions, 67 deletions
diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index f407a6b35d3d..8d8df744f7a5 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); |
| @@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) | |||
| 1509 | struct pl330_thread *thrd = ch_id; | 1496 | struct pl330_thread *thrd = ch_id; |
| 1510 | struct pl330_dmac *pl330; | 1497 | struct pl330_dmac *pl330; |
| 1511 | unsigned long flags; | 1498 | unsigned long flags; |
| 1512 | int ret = 0, active; | 1499 | int ret = 0, active = thrd->req_running; |
| 1513 | 1500 | ||
| 1514 | if (!thrd || thrd->free || thrd->dmac->state == DYING) | 1501 | if (!thrd || thrd->free || thrd->dmac->state == DYING) |
| 1515 | return -EINVAL; | 1502 | return -EINVAL; |
| @@ -1525,28 +1512,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) | |||
| 1525 | 1512 | ||
| 1526 | thrd->req[0].r = NULL; | 1513 | thrd->req[0].r = NULL; |
| 1527 | thrd->req[1].r = NULL; | 1514 | thrd->req[1].r = NULL; |
| 1528 | MARK_FREE(&thrd->req[0]); | 1515 | mark_free(thrd, 0); |
| 1529 | MARK_FREE(&thrd->req[1]); | 1516 | mark_free(thrd, 1); |
| 1530 | break; | 1517 | break; |
| 1531 | 1518 | ||
| 1532 | case PL330_OP_ABORT: | 1519 | case PL330_OP_ABORT: |
| 1533 | active = _thrd_active(thrd); | ||
| 1534 | |||
| 1535 | /* Make sure the channel is stopped */ | 1520 | /* Make sure the channel is stopped */ |
| 1536 | _stop(thrd); | 1521 | _stop(thrd); |
| 1537 | 1522 | ||
| 1538 | /* ABORT is only for the active req */ | 1523 | /* ABORT is only for the active req */ |
| 1539 | if (!active) | 1524 | if (active == -1) |
| 1540 | break; | 1525 | break; |
| 1541 | 1526 | ||
| 1542 | active--; | ||
| 1543 | |||
| 1544 | thrd->req[active].r = NULL; | 1527 | thrd->req[active].r = NULL; |
| 1545 | MARK_FREE(&thrd->req[active]); | 1528 | mark_free(thrd, active); |
| 1546 | 1529 | ||
| 1547 | /* Start the next */ | 1530 | /* Start the next */ |
| 1548 | case PL330_OP_START: | 1531 | case PL330_OP_START: |
| 1549 | if (!_thrd_active(thrd) && !_start(thrd)) | 1532 | if ((active == -1) && !_start(thrd)) |
| 1550 | ret = -EIO; | 1533 | ret = -EIO; |
| 1551 | break; | 1534 | break; |
| 1552 | 1535 | ||
| @@ -1587,14 +1570,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) | |||
| 1587 | else | 1570 | else |
| 1588 | pstatus->faulting = false; | 1571 | pstatus->faulting = false; |
| 1589 | 1572 | ||
| 1590 | active = _thrd_active(thrd); | 1573 | active = thrd->req_running; |
| 1591 | 1574 | ||
| 1592 | if (!active) { | 1575 | if (active == -1) { |
| 1593 | /* Indicate that the thread is not running */ | 1576 | /* Indicate that the thread is not running */ |
| 1594 | pstatus->top_req = NULL; | 1577 | pstatus->top_req = NULL; |
| 1595 | pstatus->wait_req = NULL; | 1578 | pstatus->wait_req = NULL; |
| 1596 | } else { | 1579 | } else { |
| 1597 | active--; | ||
| 1598 | pstatus->top_req = thrd->req[active].r; | 1580 | pstatus->top_req = thrd->req[active].r; |
| 1599 | pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) | 1581 | pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) |
| 1600 | ? thrd->req[1 - active].r : NULL; | 1582 | ? thrd->req[1 - active].r : NULL; |
| @@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi) | |||
| 1659 | thrd->free = false; | 1641 | thrd->free = false; |
| 1660 | thrd->lstenq = 1; | 1642 | thrd->lstenq = 1; |
| 1661 | thrd->req[0].r = NULL; | 1643 | thrd->req[0].r = NULL; |
| 1662 | MARK_FREE(&thrd->req[0]); | 1644 | mark_free(thrd, 0); |
| 1663 | thrd->req[1].r = NULL; | 1645 | thrd->req[1].r = NULL; |
| 1664 | MARK_FREE(&thrd->req[1]); | 1646 | mark_free(thrd, 1); |
| 1665 | break; | 1647 | break; |
| 1666 | } | 1648 | } |
| 1667 | } | 1649 | } |
| @@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd) | |||
| 1767 | thrd->req[0].mc_bus = pl330->mcode_bus | 1749 | thrd->req[0].mc_bus = pl330->mcode_bus |
| 1768 | + (thrd->id * pi->mcbufsz); | 1750 | + (thrd->id * pi->mcbufsz); |
| 1769 | thrd->req[0].r = NULL; | 1751 | thrd->req[0].r = NULL; |
| 1770 | MARK_FREE(&thrd->req[0]); | 1752 | mark_free(thrd, 0); |
| 1771 | 1753 | ||
| 1772 | thrd->req[1].mc_cpu = thrd->req[0].mc_cpu | 1754 | thrd->req[1].mc_cpu = thrd->req[0].mc_cpu |
| 1773 | + pi->mcbufsz / 2; | 1755 | + pi->mcbufsz / 2; |
| 1774 | thrd->req[1].mc_bus = thrd->req[0].mc_bus | 1756 | thrd->req[1].mc_bus = thrd->req[0].mc_bus |
| 1775 | + pi->mcbufsz / 2; | 1757 | + pi->mcbufsz / 2; |
| 1776 | thrd->req[1].r = NULL; | 1758 | thrd->req[1].r = NULL; |
| 1777 | MARK_FREE(&thrd->req[1]); | 1759 | mark_free(thrd, 1); |
| 1778 | } | 1760 | } |
| 1779 | 1761 | ||
| 1780 | static int dmac_alloc_threads(struct pl330_dmac *pl330) | 1762 | static int dmac_alloc_threads(struct pl330_dmac *pl330) |
