From 90738bbf1456c06a96b98ddc7ce0e7e44214eba6 Mon Sep 17 00:00:00 2001 From: hhb Date: Tue, 28 Aug 2012 16:33:15 +0800 Subject: [PATCH] dma pl330: add dma infiniteloop transfer --- arch/arm/common/pl330.c | 134 ++++++++++++++++++---- arch/arm/include/asm/hardware/pl330.h | 2 + arch/arm/plat-rk/dma-pl330.c | 52 ++++++--- arch/arm/plat-rk/include/plat/dma-pl330.h | 18 ++- 4 files changed, 164 insertions(+), 42 deletions(-) diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index 7dc18b42d510..f793e7782839 100644 --- a/arch/arm/common/pl330.c +++ b/arch/arm/common/pl330.c @@ -379,11 +379,11 @@ static inline u32 get_id(struct pl330_info *pi, u32 off) void __iomem *regs = pi->base; u32 id = 0; -#ifdef CONFIG_PLAT_RK +#ifdef CONFIG_ARCH_RK30 id |= ((readl(regs + off + 0x0) & 0xff) << 0); - id |= ((readl(regs + off + 0x4) & 0xff) << 8); + id |= ((readl(regs + off + 0x4) & 0xff)<< 8); id |= ((readl(regs + off + 0x8) & 0xff) << 16); - id |= ((readl(regs + off + 0xc) & 0xff) << 24); + id |= ((readl(regs + off + 0xc) & 0xff)<< 24); #else id |= (readb(regs + off + 0x0) << 0); id |= (readb(regs + off + 0x4) << 8); @@ -1125,6 +1125,62 @@ 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; + //hhb + /* Max iterations possible in DMALP is 256 */ + if (bursts > 256) { + lcnt1 = 256; + cyc = bursts/256; //cyc shuold be less than 8 + } else { + lcnt1 = bursts; + cyc = 1; + } + + /* 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); + if(pxs->r->infiniteloop_sev) //may be we don't need interrupt when dma transfer + off += _emit_SEV(dry_run, &buf[off], ev); + /* end loop1 */ + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = off - ljmp0; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + /* end loop0 */ + 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) { @@ -1159,6 +1215,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. @@ -1177,22 +1247,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; } @@ -1290,17 +1371,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)) { @@ -1477,10 +1558,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); diff --git a/arch/arm/include/asm/hardware/pl330.h b/arch/arm/include/asm/hardware/pl330.h index 545a521b333b..407d0f9e7fc3 100644 --- a/arch/arm/include/asm/hardware/pl330.h +++ b/arch/arm/include/asm/hardware/pl330.h @@ -165,6 +165,8 @@ struct pl330_req { struct pl330_reqcfg *cfg; /* Pointer to first xfer in the request. */ struct pl330_xfer *x; + unsigned int infiniteloop; + unsigned int infiniteloop_sev; }; /* diff --git a/arch/arm/plat-rk/dma-pl330.c b/arch/arm/plat-rk/dma-pl330.c index 7786f82d4c64..de8191f0c7c5 100644 --- a/arch/arm/plat-rk/dma-pl330.c +++ b/arch/arm/plat-rk/dma-pl330.c @@ -64,7 +64,7 @@ struct rk29_pl330_xfer { * @req: Two requests to communicate with the PL330 engine. * @callback_fn: Callback function to the client. * @rqcfg: Channel configuration for the xfers. - * @xfer_head: Pointer to the xfer to be next excecuted. + * @xfer_head: Pointer to the xfer to be next executed. * @dmac: Pointer to the DMAC that manages this channel, NULL if the * channel is available to be acquired. * @client: Client of this channel. NULL if the @@ -498,11 +498,12 @@ static void rk29_pl330_rq(struct rk29_pl330_chan *ch, enum rk29_dma_buffresult res; spin_lock_irqsave(&res_lock, flags); - xl = r->x; - r->x = NULL; + if (!r->infiniteloop) { + r->x = NULL; - rk29_pl330_submit(ch, r); + rk29_pl330_submit(ch, r); + } spin_unlock_irqrestore(&res_lock, flags); @@ -515,12 +516,20 @@ static void rk29_pl330_rq(struct rk29_pl330_chan *ch, res = RK29_RES_ERR; /* If last request had some xfer */ - if (xl) { - xfer = container_of(xl, struct rk29_pl330_xfer, px); - _finish_off(xfer, res, 0); + if (!r->infiniteloop) { + if (xl) { + xfer = container_of(xl, struct rk29_pl330_xfer, px); + _finish_off(xfer, res, 0); + } else { + dev_info(ch->dmac->pi->dev, "%s:%d No Xfer?!\n", + __func__, __LINE__); + } } else { - dev_info(ch->dmac->pi->dev, "%s:%d No Xfer?!\n", - __func__, __LINE__); + /* Do callback */ + + xfer = container_of(xl, struct rk29_pl330_xfer, px); + if (ch->callback_fn) + ch->callback_fn(xfer->token, xfer->px.bytes, res); } } @@ -661,9 +670,9 @@ ctrl_exit: return ret; } EXPORT_SYMBOL(rk29_dma_ctrl); - -int rk29_dma_enqueue(enum dma_ch id, void *token, - dma_addr_t addr, int size) +//hhb@rock-chips.com 2012-06-14 +int rk29_dma_enqueue_ring(enum dma_ch id, void *token, + dma_addr_t addr, int size, int numofblock, bool sev) { struct rk29_pl330_chan *ch; struct rk29_pl330_xfer *xfer; @@ -711,11 +720,17 @@ int rk29_dma_enqueue(enum dma_ch id, void *token, /* Try submitting on either request */ idx = (ch->lrq == &ch->req[0]) ? 1 : 0; - if (!ch->req[idx].x) + if (!ch->req[idx].x) { + ch->req[idx].infiniteloop = numofblock; + if(numofblock) + ch->req[idx].infiniteloop_sev = sev; rk29_pl330_submit(ch, &ch->req[idx]); - else + } else { + ch->req[1 - idx].infiniteloop = numofblock; + if(numofblock) + ch->req[1 - idx].infiniteloop_sev = sev; rk29_pl330_submit(ch, &ch->req[1 - idx]); - + } spin_unlock_irqrestore(&res_lock, flags); if (ch->options & RK29_DMAF_AUTOSTART) @@ -728,6 +743,13 @@ enq_exit: return ret; } +EXPORT_SYMBOL(rk29_dma_enqueue_ring); + +int rk29_dma_enqueue(enum dma_ch id, void *token, + dma_addr_t addr, int size) +{ + return rk29_dma_enqueue_ring(id, token, addr, size, 0, false); +} EXPORT_SYMBOL(rk29_dma_enqueue); int rk29_dma_request(enum dma_ch id, diff --git a/arch/arm/plat-rk/include/plat/dma-pl330.h b/arch/arm/plat-rk/include/plat/dma-pl330.h index 69a1649a9158..58c91b0e2413 100644 --- a/arch/arm/plat-rk/include/plat/dma-pl330.h +++ b/arch/arm/plat-rk/include/plat/dma-pl330.h @@ -102,7 +102,10 @@ static inline bool rk29_dma_has_circular(void) { return true; } - +static inline bool rk29_dma_has_infiniteloop(void) +{ + return true; +} /* * Every PL330 DMAC has max 32 peripheral interfaces, * of which some may be not be really used in your @@ -151,6 +154,16 @@ extern int rk29_dma_setflags(unsigned int channel, extern int rk29_dma_free(unsigned int channel, struct rk29_dma_client *); +/* rk29_dma_enqueue_ring + * + * place the given buffer onto the queue of operations for the channel. + * The buffer must be allocated from dma coherent memory, or the Dcache/WB + * drained before the buffer is given to the DMA system. +*/ + +extern int rk29_dma_enqueue_ring(enum dma_ch channel, void *id, + dma_addr_t data, int size, int numofblock, bool sev); + /* rk29_dma_enqueue * * place the given buffer onto the queue of operations for the channel. @@ -158,9 +171,10 @@ extern int rk29_dma_free(unsigned int channel, struct rk29_dma_client *); * drained before the buffer is given to the DMA system. */ -extern int rk29_dma_enqueue(unsigned int channel, void *id, +extern int rk29_dma_enqueue(enum dma_ch channel, void *id, dma_addr_t data, int size); + /* rk29_dma_config * * configure the dma channel -- 2.34.1