Skip to content

Commit

Permalink
ARM: 7237/1: PL330: Fix driver freeze
Browse files Browse the repository at this point in the history
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: Javi Merino <javi.merino@arm.com>
Acked-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Tushar Behera <tushar.behera@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Javi Merino authored and Russell King committed Dec 23, 2011
1 parent ba90c51 commit abb959f
Showing 1 changed file with 49 additions and 67 deletions.
116 changes: 49 additions & 67 deletions arch/arm/common/pl330.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,6 @@
*/
#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 */
#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND)

Expand Down Expand Up @@ -301,8 +290,10 @@ struct pl330_thread {
struct pl330_dmac *dmac;
/* Only two at a time */
struct _pl330_req req[2];
/* Index of the last submitted request */
/* Index of the last enqueued request */
unsigned lstenq;
/* Index of the last submitted request or -1 if the DMA is stopped */
int req_running;
};

enum pl330_dmac_state {
Expand Down Expand Up @@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd,
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)
{
void __iomem *regs = thrd->dmac->pinfo->base;
Expand Down Expand Up @@ -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)
{
void __iomem *regs = thrd->dmac->pinfo->base;
Expand Down Expand Up @@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd)
struct _arg_GO go;
unsigned ns;
u8 insn[6] = {0, 0, 0, 0, 0, 0};
int idx;

/* Return if already ACTIVE */
if (_state(thrd) != PL330_STATE_STOPPED)
return true;

if (!IS_FREE(&thrd->req[1 - thrd->lstenq]))
req = &thrd->req[1 - thrd->lstenq];
else if (!IS_FREE(&thrd->req[thrd->lstenq]))
req = &thrd->req[thrd->lstenq];
else
req = NULL;
idx = 1 - thrd->lstenq;
if (!IS_FREE(&thrd->req[idx]))
req = &thrd->req[idx];
else {
idx = thrd->lstenq;
if (!IS_FREE(&thrd->req[idx]))
req = &thrd->req[idx];
else
req = NULL;
}

/* Return if no request */
if (!req || !req->r)
Expand Down Expand Up @@ -933,6 +920,8 @@ static bool _trigger(struct pl330_thread *thrd)
/* Only manager can execute GO */
_execute_DBGINSN(thrd, insn, true);

thrd->req_running = idx;

return true;
}

Expand Down Expand Up @@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data)

thrd->req[0].r = NULL;
thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[0]);
MARK_FREE(&thrd->req[1]);
mark_free(thrd, 0);
mark_free(thrd, 1);

/* Clear the reset flag */
pl330->dmac_tbd.reset_chan &= ~(1 << i);
Expand Down Expand Up @@ -1461,14 +1450,12 @@ int pl330_update(const struct pl330_info *pi)

thrd = &pl330->channels[id];

active = _thrd_active(thrd);
if (!active) /* Aborted */
active = thrd->req_running;
if (active == -1) /* Aborted */
continue;

active -= 1;

rqdone = &thrd->req[active];
MARK_FREE(rqdone);
mark_free(thrd, active);

/* Get going again ASAP */
_start(thrd);
Expand Down Expand Up @@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
struct pl330_thread *thrd = ch_id;
struct pl330_dmac *pl330;
unsigned long flags;
int ret = 0, active;
int ret = 0, active = thrd->req_running;

if (!thrd || thrd->free || thrd->dmac->state == DYING)
return -EINVAL;
Expand All @@ -1525,28 +1512,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)

thrd->req[0].r = NULL;
thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[0]);
MARK_FREE(&thrd->req[1]);
mark_free(thrd, 0);
mark_free(thrd, 1);
break;

case PL330_OP_ABORT:
active = _thrd_active(thrd);

/* Make sure the channel is stopped */
_stop(thrd);

/* ABORT is only for the active req */
if (!active)
if (active == -1)
break;

active--;

thrd->req[active].r = NULL;
MARK_FREE(&thrd->req[active]);
mark_free(thrd, active);

/* Start the next */
case PL330_OP_START:
if (!_thrd_active(thrd) && !_start(thrd))
if ((active == -1) && !_start(thrd))
ret = -EIO;
break;

Expand Down Expand Up @@ -1587,14 +1570,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus)
else
pstatus->faulting = false;

active = _thrd_active(thrd);
active = thrd->req_running;

if (!active) {
if (active == -1) {
/* Indicate that the thread is not running */
pstatus->top_req = NULL;
pstatus->wait_req = NULL;
} else {
active--;
pstatus->top_req = thrd->req[active].r;
pstatus->wait_req = !IS_FREE(&thrd->req[1 - active])
? thrd->req[1 - active].r : NULL;
Expand Down Expand Up @@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi)
thrd->free = false;
thrd->lstenq = 1;
thrd->req[0].r = NULL;
MARK_FREE(&thrd->req[0]);
mark_free(thrd, 0);
thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[1]);
mark_free(thrd, 1);
break;
}
}
Expand Down Expand Up @@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd)
thrd->req[0].mc_bus = pl330->mcode_bus
+ (thrd->id * pi->mcbufsz);
thrd->req[0].r = NULL;
MARK_FREE(&thrd->req[0]);
mark_free(thrd, 0);

thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
+ pi->mcbufsz / 2;
thrd->req[1].mc_bus = thrd->req[0].mc_bus
+ pi->mcbufsz / 2;
thrd->req[1].r = NULL;
MARK_FREE(&thrd->req[1]);
mark_free(thrd, 1);
}

static int dmac_alloc_threads(struct pl330_dmac *pl330)
Expand Down

0 comments on commit abb959f

Please sign in to comment.