aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/common/pl330.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/common/pl330.c')
-rw-r--r--arch/arm/common/pl330.c120
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);