diff options
Diffstat (limited to 'arch/arm/common/pl330.c')
-rw-r--r-- | arch/arm/common/pl330.c | 120 |
1 files changed, 98 insertions, 22 deletions
diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index 97912fa..6ff29e5 100644 --- a/arch/arm/common/pl330.c +++ b/arch/arm/common/pl330.c @@ -1116,6 +1116,54 @@ static inline int _loop(unsigned dry_run, u8 buf[], return off; } +/* Returns bytes consumed and updates bursts */ +static inline int _loop_infiniteloop(unsigned dry_run, u8 buf[], + unsigned long bursts, const struct _xfer_spec *pxs, int ev) +{ + int cyc, off; + unsigned lcnt0, lcnt1, ljmp0, ljmp1, ljmpfe; + struct _arg_LPEND lpend; + + off = 0; + ljmpfe = off; + lcnt0 = pxs->r->infiniteloop; + lcnt1 = 256; + cyc = bursts/256; + + /* forever loop */ + off += _emit_MOV(dry_run, &buf[off], SAR, pxs->x->src_addr); + off += _emit_MOV(dry_run, &buf[off], DAR, pxs->x->dst_addr); + + /* loop0 */ + off += _emit_LP(dry_run, &buf[off], 0, lcnt0); + ljmp0 = off; + + /* loop1 */ + off += _emit_LP(dry_run, &buf[off], 1, lcnt1); + ljmp1 = off; + off += _bursts(dry_run, &buf[off], pxs, cyc); + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 1; + lpend.bjump = off - ljmp1; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + off += _emit_SEV(dry_run, &buf[off], ev); + + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = off - ljmp0; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + lpend.cond = ALWAYS; + lpend.forever = true; + lpend.loop = 1; + lpend.bjump = off - ljmpfe; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + return off; +} + static inline int _setup_loops(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs) { @@ -1150,6 +1198,20 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[], return off; } +static inline int _setup_xfer_infiniteloop(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int ev) +{ + struct pl330_xfer *x = pxs->x; + u32 ccr = pxs->ccr; + unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); + int off = 0; + + /* Setup Loop(s) */ + off += _loop_infiniteloop(dry_run, &buf[off], bursts, pxs, ev); + + return off; +} + /* * A req is a sequence of one or more xfer units. * Returns the number of bytes taken to setup the MC for the req. @@ -1168,22 +1230,33 @@ static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); x = pxs->r->x; - do { + + if (!pxs->r->infiniteloop) { + do { + /* Error if xfer length is not aligned at burst size */ + if (x->bytes % (BRST_SIZE(pxs->ccr) + * BRST_LEN(pxs->ccr))) + return -EINVAL; + + pxs->x = x; + off += _setup_xfer(dry_run, &buf[off], pxs); + + x = x->next; + } while (x); + + /* DMASEV peripheral/event */ + off += _emit_SEV(dry_run, &buf[off], thrd->ev); + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); + } else { /* Error if xfer length is not aligned at burst size */ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) return -EINVAL; pxs->x = x; - off += _setup_xfer(dry_run, &buf[off], pxs); - - x = x->next; - } while (x); - - /* DMASEV peripheral/event */ - off += _emit_SEV(dry_run, &buf[off], thrd->ev); - /* DMAEND */ - off += _emit_END(dry_run, &buf[off]); - + off += _setup_xfer_infiniteloop + (dry_run, &buf[off], pxs, thrd->ev); + } return off; } @@ -1281,17 +1354,17 @@ int pl330_submit_req(void *ch_id, struct pl330_req *r) goto xfer_exit; } - /* Prefer Secure Channel */ - if (!_manager_ns(thrd)) - r->cfg->nonsecure = 0; - else - r->cfg->nonsecure = 1; - /* Use last settings, if not provided */ - if (r->cfg) + if (r->cfg) { + /* Prefer Secure Channel */ + if (!_manager_ns(thrd)) + r->cfg->nonsecure = 0; + else + r->cfg->nonsecure = 1; ccr = _prepare_ccr(r->cfg); - else + } else { ccr = readl(regs + CC(thrd->id)); + } /* If this req doesn't have valid xfer settings */ if (!_is_valid(ccr)) { @@ -1468,10 +1541,13 @@ int pl330_update(const struct pl330_info *pi) active -= 1; rqdone = &thrd->req[active]; - MARK_FREE(rqdone); - /* Get going again ASAP */ - _start(thrd); + if (!rqdone->r->infiniteloop) { + MARK_FREE(rqdone); + + /* Get going again ASAP */ + _start(thrd); + } /* For now, just make a list of callbacks to be done */ list_add_tail(&rqdone->rqd, &pl330->req_done); |