summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiu Ying <Ying.Liu@freescale.com>2015-02-05 15:08:24 +0800
committerLiu Ying <Ying.Liu@freescale.com>2015-02-06 18:18:18 +0800
commite4c03919a77caff0f793c4af5f91096e56dba506 (patch)
tree4303faf98efd7e94bd349bb5f4a1157e96a666ea
parent16121ef8e473a4ce71accca59b006575312028e2 (diff)
MLK-10227 video: mxsfb: Correct interrupt handling
We've got a race condition bewteen the interrupt handler mxsfb_irq_handler() and the function mxsfb_wait_for_vsync() on the flag host->wait4vsync. If a CUR_FRAME_DONE interrupt comes and we just finish setting host->wait4vsync to be 1 in mxsfb_wait_for_vsync() before we go to the interrupt handler, we are likely to see the VSYNC_EDGE interrupt status bit asserted in the interrupt handler for the CUR_FRAME_DONE interrupt, disable the not yet enabled VSYNC_EDGE interrupt and finally clear host->wait4vsync. Then, we go back to mxsfb_wait_for_vsync() and enable the VSYNC_EDGE interrupt with host->wait4vsync=0. This may leave the VSYNC_EDGE interrupt enabled all the time and never get a chance to be disabled in the interrupt handler. So, we are deemed to hang up because the uncleared VSYNC_EDGE interrupt status bit will cause the CPU to be trapped forever, according to SoC designer's words. This patch corrects the interrupt handling to handle only the interrupts which are acknowledged by checking both the interrupt enablement bits and the status bits but not the status bits only. This may avoid any bogus interrupt from being handled. Signed-off-by: Liu Ying <Ying.Liu@freescale.com> (cherry picked from commit 24e1e55076b624f9dc93c1f23e14dd024bdff1c7)
-rw-r--r--drivers/video/mxsfb.c37
1 files changed, 24 insertions, 13 deletions
diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c
index 3fb54350a182..84f62a47d843 100644
--- a/drivers/video/mxsfb.c
+++ b/drivers/video/mxsfb.c
@@ -4,7 +4,7 @@
* This code is based on:
* Author: Vitaly Wool <vital@embeddedalley.com>
*
- * Copyright 2008-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -115,6 +115,16 @@
#define CTRL1_UNDERFLOW_IRQ (1 << 10)
#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9)
#define CTRL1_VSYNC_EDGE_IRQ (1 << 8)
+#define CTRL1_IRQ_ENABLE_MASK (CTRL1_OVERFLOW_IRQ_EN | \
+ CTRL1_UNDERFLOW_IRQ_EN | \
+ CTRL1_CUR_FRAME_DONE_IRQ_EN | \
+ CTRL1_VSYNC_EDGE_IRQ_EN)
+#define CTRL1_IRQ_ENABLE_SHIFT 12
+#define CTRL1_IRQ_STATUS_MASK (CTRL1_OVERFLOW_IRQ | \
+ CTRL1_UNDERFLOW_IRQ | \
+ CTRL1_CUR_FRAME_DONE_IRQ | \
+ CTRL1_VSYNC_EDGE_IRQ)
+#define CTRL1_IRQ_STATUS_SHIFT 8
#define CTRL2_OUTSTANDING_REQS__REQ_16 (3 << 21)
@@ -368,31 +378,32 @@ static inline unsigned chan_to_field(unsigned chan, struct fb_bitfield *bf)
static irqreturn_t mxsfb_irq_handler(int irq, void *dev_id)
{
struct mxsfb_info *host = dev_id;
- u32 status_lcd = readl(host->base + LCDC_CTRL1);
+ u32 ctrl1, enable, status, acked_status;
- if ((status_lcd & CTRL1_VSYNC_EDGE_IRQ) &&
- host->wait4vsync) {
+ ctrl1 = readl(host->base + LCDC_CTRL1);
+ enable = (ctrl1 & CTRL1_IRQ_ENABLE_MASK) >> CTRL1_IRQ_ENABLE_SHIFT;
+ status = (ctrl1 & CTRL1_IRQ_STATUS_MASK) >> CTRL1_IRQ_STATUS_SHIFT;
+ acked_status = (enable & status) << CTRL1_IRQ_STATUS_SHIFT;
+
+ if ((acked_status & CTRL1_VSYNC_EDGE_IRQ) && host->wait4vsync) {
writel(CTRL1_VSYNC_EDGE_IRQ_EN,
host->base + LCDC_CTRL1 + REG_CLR);
host->wait4vsync = 0;
complete(&host->vsync_complete);
}
- if (status_lcd & CTRL1_CUR_FRAME_DONE_IRQ) {
+ if (acked_status & CTRL1_CUR_FRAME_DONE_IRQ) {
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN,
host->base + LCDC_CTRL1 + REG_CLR);
up(&host->flip_sem);
}
- if (status_lcd & CTRL1_UNDERFLOW_IRQ) {
- writel(CTRL1_UNDERFLOW_IRQ,
- host->base + LCDC_CTRL1 + REG_CLR);
- }
+ if (acked_status & CTRL1_UNDERFLOW_IRQ)
+ writel(CTRL1_UNDERFLOW_IRQ, host->base + LCDC_CTRL1 + REG_CLR);
+
+ if (acked_status & CTRL1_OVERFLOW_IRQ)
+ writel(CTRL1_OVERFLOW_IRQ, host->base + LCDC_CTRL1 + REG_CLR);
- if (status_lcd & CTRL1_OVERFLOW_IRQ) {
- writel(CTRL1_OVERFLOW_IRQ,
- host->base + LCDC_CTRL1 + REG_CLR);
- }
return IRQ_HANDLED;
}