Commit abb959f8 authored by Javi Merino's avatar Javi Merino Committed by Russell King

ARM: 7237/1: PL330: Fix driver freeze

Add a req_running field to the pl330_thread to track which request (if
any) has been submitted to the DMA.  This mechanism replaces the old
one in which we tried to guess the same by looking at the PC of the
DMA, which could prevent the driver from sending more requests if it
didn't guess correctly.

Reference: <1323631637-9610-1-git-send-email-javi.merino@arm.com>
Signed-off-by: default avatarJavi Merino <javi.merino@arm.com>
Acked-by: default avatarJassi Brar <jaswinder.singh@linaro.org>
Tested-by: default avatarTushar Behera <tushar.behera@linaro.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent ba90c516
...@@ -221,17 +221,6 @@ ...@@ -221,17 +221,6 @@
*/ */
#define MCODE_BUFF_PER_REQ 256 #define MCODE_BUFF_PER_REQ 256
/*
* Mark a _pl330_req as free.
* We do it by writing DMAEND as the first instruction
* because no valid request is going to have DMAEND as
* its first instruction to execute.
*/
#define MARK_FREE(req) do { \
_emit_END(0, (req)->mc_cpu); \
(req)->mc_len = 0; \
} while (0)
/* If the _pl330_req is available to the client */ /* If the _pl330_req is available to the client */
#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND) #define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND)
...@@ -301,8 +290,10 @@ struct pl330_thread { ...@@ -301,8 +290,10 @@ struct pl330_thread {
struct pl330_dmac *dmac; struct pl330_dmac *dmac;
/* Only two at a time */ /* Only two at a time */
struct _pl330_req req[2]; struct _pl330_req req[2];
/* Index of the last submitted request */ /* Index of the last enqueued request */
unsigned lstenq; unsigned lstenq;
/* Index of the last submitted request or -1 if the DMA is stopped */
int req_running;
}; };
enum pl330_dmac_state { enum pl330_dmac_state {
...@@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd, ...@@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd,
writel(0, regs + DBGCMD); writel(0, regs + DBGCMD);
} }
/*
* Mark a _pl330_req as free.
* We do it by writing DMAEND as the first instruction
* because no valid request is going to have DMAEND as
* its first instruction to execute.
*/
static void mark_free(struct pl330_thread *thrd, int idx)
{
struct _pl330_req *req = &thrd->req[idx];
_emit_END(0, req->mc_cpu);
req->mc_len = 0;
thrd->req_running = -1;
}
static inline u32 _state(struct pl330_thread *thrd) static inline u32 _state(struct pl330_thread *thrd)
{ {
void __iomem *regs = thrd->dmac->pinfo->base; void __iomem *regs = thrd->dmac->pinfo->base;
...@@ -836,31 +843,6 @@ static inline u32 _state(struct pl330_thread *thrd) ...@@ -836,31 +843,6 @@ static inline u32 _state(struct pl330_thread *thrd)
} }
} }
/* If the request 'req' of thread 'thrd' is currently active */
static inline bool _req_active(struct pl330_thread *thrd,
struct _pl330_req *req)
{
void __iomem *regs = thrd->dmac->pinfo->base;
u32 buf = req->mc_bus, pc = readl(regs + CPC(thrd->id));
if (IS_FREE(req))
return false;
return (pc >= buf && pc <= buf + req->mc_len) ? true : false;
}
/* Returns 0 if the thread is inactive, ID of active req + 1 otherwise */
static inline unsigned _thrd_active(struct pl330_thread *thrd)
{
if (_req_active(thrd, &thrd->req[0]))
return 1; /* First req active */
if (_req_active(thrd, &thrd->req[1]))
return 2; /* Second req active */
return 0;
}
static void _stop(struct pl330_thread *thrd) static void _stop(struct pl330_thread *thrd)
{ {
void __iomem *regs = thrd->dmac->pinfo->base; void __iomem *regs = thrd->dmac->pinfo->base;
...@@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd) ...@@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd)
struct _arg_GO go; struct _arg_GO go;
unsigned ns; unsigned ns;
u8 insn[6] = {0, 0, 0, 0, 0, 0}; u8 insn[6] = {0, 0, 0, 0, 0, 0};
int idx;
/* Return if already ACTIVE */ /* Return if already ACTIVE */
if (_state(thrd) != PL330_STATE_STOPPED) if (_state(thrd) != PL330_STATE_STOPPED)
return true; return true;
if (!IS_FREE(&thrd->req[1 - thrd->lstenq])) idx = 1 - thrd->lstenq;
req = &thrd->req[1 - thrd->lstenq]; if (!IS_FREE(&thrd->req[idx]))
else if (!IS_FREE(&thrd->req[thrd->lstenq])) req = &thrd->req[idx];
req = &thrd->req[thrd->lstenq]; else {
else idx = thrd->lstenq;
req = NULL; if (!IS_FREE(&thrd->req[idx]))
req = &thrd->req[idx];
else
req = NULL;
}
/* Return if no request */ /* Return if no request */
if (!req || !req->r) if (!req || !req->r)
...@@ -933,6 +920,8 @@ static bool _trigger(struct pl330_thread *thrd) ...@@ -933,6 +920,8 @@ static bool _trigger(struct pl330_thread *thrd)
/* Only manager can execute GO */ /* Only manager can execute GO */
_execute_DBGINSN(thrd, insn, true); _execute_DBGINSN(thrd, insn, true);
thrd->req_running = idx;
return true; return true;
} }
...@@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data) ...@@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data)
thrd->req[0].r = NULL; thrd->req[0].r = NULL;
thrd->req[1].r = NULL; thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[0]); mark_free(thrd, 0);
MARK_FREE(&thrd->req[1]); mark_free(thrd, 1);
/* Clear the reset flag */ /* Clear the reset flag */
pl330->dmac_tbd.reset_chan &= ~(1 << i); pl330->dmac_tbd.reset_chan &= ~(1 << i);
...@@ -1461,14 +1450,12 @@ int pl330_update(const struct pl330_info *pi) ...@@ -1461,14 +1450,12 @@ int pl330_update(const struct pl330_info *pi)
thrd = &pl330->channels[id]; thrd = &pl330->channels[id];
active = _thrd_active(thrd); active = thrd->req_running;
if (!active) /* Aborted */ if (active == -1) /* Aborted */
continue; continue;
active -= 1;
rqdone = &thrd->req[active]; rqdone = &thrd->req[active];
MARK_FREE(rqdone); mark_free(thrd, active);
/* Get going again ASAP */ /* Get going again ASAP */
_start(thrd); _start(thrd);
...@@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) ...@@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
struct pl330_thread *thrd = ch_id; struct pl330_thread *thrd = ch_id;
struct pl330_dmac *pl330; struct pl330_dmac *pl330;
unsigned long flags; unsigned long flags;
int ret = 0, active; int ret = 0, active = thrd->req_running;
if (!thrd || thrd->free || thrd->dmac->state == DYING) if (!thrd || thrd->free || thrd->dmac->state == DYING)
return -EINVAL; return -EINVAL;
...@@ -1525,28 +1512,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) ...@@ -1525,28 +1512,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
thrd->req[0].r = NULL; thrd->req[0].r = NULL;
thrd->req[1].r = NULL; thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[0]); mark_free(thrd, 0);
MARK_FREE(&thrd->req[1]); mark_free(thrd, 1);
break; break;
case PL330_OP_ABORT: case PL330_OP_ABORT:
active = _thrd_active(thrd);
/* Make sure the channel is stopped */ /* Make sure the channel is stopped */
_stop(thrd); _stop(thrd);
/* ABORT is only for the active req */ /* ABORT is only for the active req */
if (!active) if (active == -1)
break; break;
active--;
thrd->req[active].r = NULL; thrd->req[active].r = NULL;
MARK_FREE(&thrd->req[active]); mark_free(thrd, active);
/* Start the next */ /* Start the next */
case PL330_OP_START: case PL330_OP_START:
if (!_thrd_active(thrd) && !_start(thrd)) if ((active == -1) && !_start(thrd))
ret = -EIO; ret = -EIO;
break; break;
...@@ -1587,14 +1570,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) ...@@ -1587,14 +1570,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus)
else else
pstatus->faulting = false; pstatus->faulting = false;
active = _thrd_active(thrd); active = thrd->req_running;
if (!active) { if (active == -1) {
/* Indicate that the thread is not running */ /* Indicate that the thread is not running */
pstatus->top_req = NULL; pstatus->top_req = NULL;
pstatus->wait_req = NULL; pstatus->wait_req = NULL;
} else { } else {
active--;
pstatus->top_req = thrd->req[active].r; pstatus->top_req = thrd->req[active].r;
pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) pstatus->wait_req = !IS_FREE(&thrd->req[1 - active])
? thrd->req[1 - active].r : NULL; ? thrd->req[1 - active].r : NULL;
...@@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi) ...@@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi)
thrd->free = false; thrd->free = false;
thrd->lstenq = 1; thrd->lstenq = 1;
thrd->req[0].r = NULL; thrd->req[0].r = NULL;
MARK_FREE(&thrd->req[0]); mark_free(thrd, 0);
thrd->req[1].r = NULL; thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[1]); mark_free(thrd, 1);
break; break;
} }
} }
...@@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd) ...@@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd)
thrd->req[0].mc_bus = pl330->mcode_bus thrd->req[0].mc_bus = pl330->mcode_bus
+ (thrd->id * pi->mcbufsz); + (thrd->id * pi->mcbufsz);
thrd->req[0].r = NULL; thrd->req[0].r = NULL;
MARK_FREE(&thrd->req[0]); mark_free(thrd, 0);
thrd->req[1].mc_cpu = thrd->req[0].mc_cpu thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
+ pi->mcbufsz / 2; + pi->mcbufsz / 2;
thrd->req[1].mc_bus = thrd->req[0].mc_bus thrd->req[1].mc_bus = thrd->req[0].mc_bus
+ pi->mcbufsz / 2; + pi->mcbufsz / 2;
thrd->req[1].r = NULL; thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[1]); mark_free(thrd, 1);
} }
static int dmac_alloc_threads(struct pl330_dmac *pl330) static int dmac_alloc_threads(struct pl330_dmac *pl330)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment