Merge branch 'fixes' of http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-2.6-arm
* 'fixes' of http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-2.6-arm: ARM: 7237/1: PL330: Fix driver freeze ARM: 7197/1: errata: Remove SMP dependency for erratum 751472 ARM: 7196/1: errata: Remove SMP dependency for erratum 720789 ARM: 7220/1: mmc: mmci: Fixup error handling for dma ARM: 7214/1: mmc: mmci: Fixup handling of MCI_STARTBITERR
This commit is contained in:
commit
06867fbb8a
4 changed files with 65 additions and 75 deletions
|
@ -1246,7 +1246,7 @@ config PL310_ERRATA_588369
|
||||||
|
|
||||||
config ARM_ERRATA_720789
|
config ARM_ERRATA_720789
|
||||||
bool "ARM errata: TLBIASIDIS and TLBIMVAIS operations can broadcast a faulty ASID"
|
bool "ARM errata: TLBIASIDIS and TLBIMVAIS operations can broadcast a faulty ASID"
|
||||||
depends on CPU_V7 && SMP
|
depends on CPU_V7
|
||||||
help
|
help
|
||||||
This option enables the workaround for the 720789 Cortex-A9 (prior to
|
This option enables the workaround for the 720789 Cortex-A9 (prior to
|
||||||
r2p0) erratum. A faulty ASID can be sent to the other CPUs for the
|
r2p0) erratum. A faulty ASID can be sent to the other CPUs for the
|
||||||
|
@ -1282,7 +1282,7 @@ config ARM_ERRATA_743622
|
||||||
|
|
||||||
config ARM_ERRATA_751472
|
config ARM_ERRATA_751472
|
||||||
bool "ARM errata: Interrupted ICIALLUIS may prevent completion of broadcasted operation"
|
bool "ARM errata: Interrupted ICIALLUIS may prevent completion of broadcasted operation"
|
||||||
depends on CPU_V7 && SMP
|
depends on CPU_V7
|
||||||
help
|
help
|
||||||
This option enables the workaround for the 751472 Cortex-A9 (prior
|
This option enables the workaround for the 751472 Cortex-A9 (prior
|
||||||
to r3p0) erratum. An interrupted ICIALLUIS operation may prevent the
|
to r3p0) erratum. An interrupted ICIALLUIS operation may prevent the
|
||||||
|
|
|
@ -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 {
|
||||||
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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
|
@ -363,11 +363,13 @@ __v7_setup:
|
||||||
orreq r10, r10, #1 << 6 @ set bit #6
|
orreq r10, r10, #1 << 6 @ set bit #6
|
||||||
mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register
|
mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ARM_ERRATA_751472
|
#if defined(CONFIG_ARM_ERRATA_751472) && defined(CONFIG_SMP)
|
||||||
cmp r6, #0x30 @ present prior to r3p0
|
ALT_SMP(cmp r6, #0x30) @ present prior to r3p0
|
||||||
|
ALT_UP_B(1f)
|
||||||
mrclt p15, 0, r10, c15, c0, 1 @ read diagnostic register
|
mrclt p15, 0, r10, c15, c0, 1 @ read diagnostic register
|
||||||
orrlt r10, r10, #1 << 11 @ set bit #11
|
orrlt r10, r10, #1 << 11 @ set bit #11
|
||||||
mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register
|
mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register
|
||||||
|
1:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
3: mov r10, #0
|
3: mov r10, #0
|
||||||
|
|
|
@ -675,7 +675,8 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
||||||
unsigned int status)
|
unsigned int status)
|
||||||
{
|
{
|
||||||
/* First check for errors */
|
/* First check for errors */
|
||||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
|
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
|
||||||
|
MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
|
||||||
u32 remain, success;
|
u32 remain, success;
|
||||||
|
|
||||||
/* Terminate the DMA transfer */
|
/* Terminate the DMA transfer */
|
||||||
|
@ -754,8 +755,12 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd->data || cmd->error) {
|
if (!cmd->data || cmd->error) {
|
||||||
if (host->data)
|
if (host->data) {
|
||||||
|
/* Terminate the DMA transfer */
|
||||||
|
if (dma_inprogress(host))
|
||||||
|
mmci_dma_data_error(host);
|
||||||
mmci_stop_data(host);
|
mmci_stop_data(host);
|
||||||
|
}
|
||||||
mmci_request_end(host, cmd->mrq);
|
mmci_request_end(host, cmd->mrq);
|
||||||
} else if (!(cmd->data->flags & MMC_DATA_READ)) {
|
} else if (!(cmd->data->flags & MMC_DATA_READ)) {
|
||||||
mmci_start_data(host, cmd->data);
|
mmci_start_data(host, cmd->data);
|
||||||
|
@ -955,8 +960,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
|
||||||
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
|
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
|
||||||
|
|
||||||
data = host->data;
|
data = host->data;
|
||||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
|
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
|
||||||
MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
|
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
|
||||||
|
MCI_DATABLOCKEND) && data)
|
||||||
mmci_data_irq(host, data, status);
|
mmci_data_irq(host, data, status);
|
||||||
|
|
||||||
cmd = host->cmd;
|
cmd = host->cmd;
|
||||||
|
|
Loading…
Reference in a new issue