Skip to content

Commit abb959f

Browse files
Javi MerinoRussell King
authored andcommitted
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: <[email protected]> Signed-off-by: Javi Merino <[email protected]> Acked-by: Jassi Brar <[email protected]> Tested-by: Tushar Behera <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent ba90c51 commit abb959f

File tree

1 file changed

+49
-67
lines changed

1 file changed

+49
-67
lines changed

arch/arm/common/pl330.c

Lines changed: 49 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,6 @@
221221
*/
222222
#define MCODE_BUFF_PER_REQ 256
223223

224-
/*
225-
* Mark a _pl330_req as free.
226-
* We do it by writing DMAEND as the first instruction
227-
* because no valid request is going to have DMAEND as
228-
* its first instruction to execute.
229-
*/
230-
#define MARK_FREE(req) do { \
231-
_emit_END(0, (req)->mc_cpu); \
232-
(req)->mc_len = 0; \
233-
} while (0)
234-
235224
/* If the _pl330_req is available to the client */
236225
#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND)
237226

@@ -301,8 +290,10 @@ struct pl330_thread {
301290
struct pl330_dmac *dmac;
302291
/* Only two at a time */
303292
struct _pl330_req req[2];
304-
/* Index of the last submitted request */
293+
/* Index of the last enqueued request */
305294
unsigned lstenq;
295+
/* Index of the last submitted request or -1 if the DMA is stopped */
296+
int req_running;
306297
};
307298

308299
enum pl330_dmac_state {
@@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd,
778769
writel(0, regs + DBGCMD);
779770
}
780771

772+
/*
773+
* Mark a _pl330_req as free.
774+
* We do it by writing DMAEND as the first instruction
775+
* because no valid request is going to have DMAEND as
776+
* its first instruction to execute.
777+
*/
778+
static void mark_free(struct pl330_thread *thrd, int idx)
779+
{
780+
struct _pl330_req *req = &thrd->req[idx];
781+
782+
_emit_END(0, req->mc_cpu);
783+
req->mc_len = 0;
784+
785+
thrd->req_running = -1;
786+
}
787+
781788
static inline u32 _state(struct pl330_thread *thrd)
782789
{
783790
void __iomem *regs = thrd->dmac->pinfo->base;
@@ -836,31 +843,6 @@ static inline u32 _state(struct pl330_thread *thrd)
836843
}
837844
}
838845

839-
/* If the request 'req' of thread 'thrd' is currently active */
840-
static inline bool _req_active(struct pl330_thread *thrd,
841-
struct _pl330_req *req)
842-
{
843-
void __iomem *regs = thrd->dmac->pinfo->base;
844-
u32 buf = req->mc_bus, pc = readl(regs + CPC(thrd->id));
845-
846-
if (IS_FREE(req))
847-
return false;
848-
849-
return (pc >= buf && pc <= buf + req->mc_len) ? true : false;
850-
}
851-
852-
/* Returns 0 if the thread is inactive, ID of active req + 1 otherwise */
853-
static inline unsigned _thrd_active(struct pl330_thread *thrd)
854-
{
855-
if (_req_active(thrd, &thrd->req[0]))
856-
return 1; /* First req active */
857-
858-
if (_req_active(thrd, &thrd->req[1]))
859-
return 2; /* Second req active */
860-
861-
return 0;
862-
}
863-
864846
static void _stop(struct pl330_thread *thrd)
865847
{
866848
void __iomem *regs = thrd->dmac->pinfo->base;
@@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd)
892874
struct _arg_GO go;
893875
unsigned ns;
894876
u8 insn[6] = {0, 0, 0, 0, 0, 0};
877+
int idx;
895878

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

900-
if (!IS_FREE(&thrd->req[1 - thrd->lstenq]))
901-
req = &thrd->req[1 - thrd->lstenq];
902-
else if (!IS_FREE(&thrd->req[thrd->lstenq]))
903-
req = &thrd->req[thrd->lstenq];
904-
else
905-
req = NULL;
883+
idx = 1 - thrd->lstenq;
884+
if (!IS_FREE(&thrd->req[idx]))
885+
req = &thrd->req[idx];
886+
else {
887+
idx = thrd->lstenq;
888+
if (!IS_FREE(&thrd->req[idx]))
889+
req = &thrd->req[idx];
890+
else
891+
req = NULL;
892+
}
906893

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

923+
thrd->req_running = idx;
924+
936925
return true;
937926
}
938927

@@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data)
13821371

13831372
thrd->req[0].r = NULL;
13841373
thrd->req[1].r = NULL;
1385-
MARK_FREE(&thrd->req[0]);
1386-
MARK_FREE(&thrd->req[1]);
1374+
mark_free(thrd, 0);
1375+
mark_free(thrd, 1);
13871376

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

14621451
thrd = &pl330->channels[id];
14631452

1464-
active = _thrd_active(thrd);
1465-
if (!active) /* Aborted */
1453+
active = thrd->req_running;
1454+
if (active == -1) /* Aborted */
14661455
continue;
14671456

1468-
active -= 1;
1469-
14701457
rqdone = &thrd->req[active];
1471-
MARK_FREE(rqdone);
1458+
mark_free(thrd, active);
14721459

14731460
/* Get going again ASAP */
14741461
_start(thrd);
@@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
15091496
struct pl330_thread *thrd = ch_id;
15101497
struct pl330_dmac *pl330;
15111498
unsigned long flags;
1512-
int ret = 0, active;
1499+
int ret = 0, active = thrd->req_running;
15131500

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

15261513
thrd->req[0].r = NULL;
15271514
thrd->req[1].r = NULL;
1528-
MARK_FREE(&thrd->req[0]);
1529-
MARK_FREE(&thrd->req[1]);
1515+
mark_free(thrd, 0);
1516+
mark_free(thrd, 1);
15301517
break;
15311518

15321519
case PL330_OP_ABORT:
1533-
active = _thrd_active(thrd);
1534-
15351520
/* Make sure the channel is stopped */
15361521
_stop(thrd);
15371522

15381523
/* ABORT is only for the active req */
1539-
if (!active)
1524+
if (active == -1)
15401525
break;
15411526

1542-
active--;
1543-
15441527
thrd->req[active].r = NULL;
1545-
MARK_FREE(&thrd->req[active]);
1528+
mark_free(thrd, active);
15461529

15471530
/* Start the next */
15481531
case PL330_OP_START:
1549-
if (!_thrd_active(thrd) && !_start(thrd))
1532+
if ((active == -1) && !_start(thrd))
15501533
ret = -EIO;
15511534
break;
15521535

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

1590-
active = _thrd_active(thrd);
1573+
active = thrd->req_running;
15911574

1592-
if (!active) {
1575+
if (active == -1) {
15931576
/* Indicate that the thread is not running */
15941577
pstatus->top_req = NULL;
15951578
pstatus->wait_req = NULL;
15961579
} else {
1597-
active--;
15981580
pstatus->top_req = thrd->req[active].r;
15991581
pstatus->wait_req = !IS_FREE(&thrd->req[1 - active])
16001582
? thrd->req[1 - active].r : NULL;
@@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi)
16591641
thrd->free = false;
16601642
thrd->lstenq = 1;
16611643
thrd->req[0].r = NULL;
1662-
MARK_FREE(&thrd->req[0]);
1644+
mark_free(thrd, 0);
16631645
thrd->req[1].r = NULL;
1664-
MARK_FREE(&thrd->req[1]);
1646+
mark_free(thrd, 1);
16651647
break;
16661648
}
16671649
}
@@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd)
17671749
thrd->req[0].mc_bus = pl330->mcode_bus
17681750
+ (thrd->id * pi->mcbufsz);
17691751
thrd->req[0].r = NULL;
1770-
MARK_FREE(&thrd->req[0]);
1752+
mark_free(thrd, 0);
17711753

17721754
thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
17731755
+ pi->mcbufsz / 2;
17741756
thrd->req[1].mc_bus = thrd->req[0].mc_bus
17751757
+ pi->mcbufsz / 2;
17761758
thrd->req[1].r = NULL;
1777-
MARK_FREE(&thrd->req[1]);
1759+
mark_free(thrd, 1);
17781760
}
17791761

17801762
static int dmac_alloc_threads(struct pl330_dmac *pl330)

0 commit comments

Comments
 (0)