From patchwork Mon Apr 14 11:48:42 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 3980021 Return-Path: X-Original-To: patchwork-linux-fbdev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id CC5EEBFF02 for ; Mon, 14 Apr 2014 11:49:20 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D4785201BF for ; Mon, 14 Apr 2014 11:49:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BDD1F201B9 for ; Mon, 14 Apr 2014 11:49:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754903AbaDNLtN (ORCPT ); Mon, 14 Apr 2014 07:49:13 -0400 Received: from devils.ext.ti.com ([198.47.26.153]:33136 "EHLO devils.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754867AbaDNLtJ (ORCPT ); Mon, 14 Apr 2014 07:49:09 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id s3EBn8q5017864; Mon, 14 Apr 2014 06:49:08 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id s3EBn8Qf003579; Mon, 14 Apr 2014 06:49:08 -0500 Received: from dlep33.itg.ti.com (157.170.170.75) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.3.174.1; Mon, 14 Apr 2014 06:49:08 -0500 Received: from deskari.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep33.itg.ti.com (8.14.3/8.13.8) with ESMTP id s3EBn6WF028055; Mon, 14 Apr 2014 06:49:07 -0500 From: Tomi Valkeinen To: , Archit Taneja CC: Mika Kuoppala , Tomi Valkeinen Subject: [PATCH] OMAPDSS: fix shared irq handlers Date: Mon, 14 Apr 2014 14:48:42 +0300 Message-ID: <1397476122-17868-1-git-send-email-tomi.valkeinen@ti.com> X-Mailer: git-send-email 1.8.3.2 MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Spam-Status: No, score=-7.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP DSS uses shared irq handlers for DISPC and DSI, because on OMAP3, the DISPC and DSI share the same irq line. However, the irq handlers presume that the hardware is enabled, which, in theory, may not be the case with shared irq handlers. So if an interrupt happens while the DISPC/DSI is off, the kernel will halt as the irq handler tries to access the DISPC/DSI registers. In practice that should never happen, as both DSI and DISPC are in the same power domain. So if there's an IRQ for one of them, the other is also enabled. However, if CONFIG_DEBUG_SHIRQ is enabled, the kernel will generate a spurious IRQ, which then causes the problem. This patch adds an is_enabled field for both DISPC and DSI, which is used to track if the HW is enabled. For DISPC the code is slightly more complex, as the users of DISPC can register the interrupt handler, and we want to hide the is_enabled handling from the users of DISPC. Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.c | 55 +++++++++++++++++++++++++++++++++++------ drivers/video/omap2/dss/dsi.c | 20 +++++++++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 2bbdb7ff7daf..b37e3fbf60cc 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -101,6 +101,8 @@ static struct { void __iomem *base; int irq; + irq_handler_t user_handler; + void *user_data; unsigned long core_clk_rate; unsigned long tv_pclk_rate; @@ -113,6 +115,8 @@ static struct { u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; const struct dispc_features *feat; + + bool is_enabled; } dispc; enum omap_color_component { @@ -3669,16 +3673,44 @@ static int __init dispc_init_features(struct platform_device *pdev) return 0; } +static irqreturn_t dispc_irq_handler(int irq, void *arg) +{ + if (!dispc.is_enabled) + return IRQ_NONE; + + return dispc.user_handler(irq, dispc.user_data); +} + int dispc_request_irq(irq_handler_t handler, void *dev_id) { - return devm_request_irq(&dispc.pdev->dev, dispc.irq, handler, - IRQF_SHARED, "OMAP DISPC", dev_id); + int r; + + if (dispc.user_handler != NULL) + return -EBUSY; + + dispc.user_handler = handler; + dispc.user_data = dev_id; + + /* ensure the dispc_irq_handler sees the values above */ + smp_wmb(); + + r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler, + IRQF_SHARED, "OMAP DISPC", &dispc); + if (r) { + dispc.user_handler = NULL; + dispc.user_data = NULL; + } + + return r; } EXPORT_SYMBOL(dispc_request_irq); void dispc_free_irq(void *dev_id) { - devm_free_irq(&dispc.pdev->dev, dispc.irq, dev_id); + devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc); + + dispc.user_handler = NULL; + dispc.user_data = NULL; } EXPORT_SYMBOL(dispc_free_irq); @@ -3750,6 +3782,12 @@ static int __exit omap_dispchw_remove(struct platform_device *pdev) static int dispc_runtime_suspend(struct device *dev) { + dispc.is_enabled = false; + /* ensure the dispc_irq_handler sees the is_enabled value */ + smp_wmb(); + /* wait for current handler to finish before turning the DISPC off */ + synchronize_irq(dispc.irq); + dispc_save_context(); return 0; @@ -3763,12 +3801,15 @@ static int dispc_runtime_resume(struct device *dev) * _omap_dispc_initial_config(). We can thus use it to detect if * we have lost register context. */ - if (REG_GET(DISPC_CONFIG, 2, 1) == OMAP_DSS_LOAD_FRAME_ONLY) - return 0; + if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { + _omap_dispc_initial_config(); - _omap_dispc_initial_config(); + dispc_restore_context(); + } - dispc_restore_context(); + dispc.is_enabled = true; + /* ensure the dispc_irq_handler sees the is_enabled value */ + smp_wmb(); return 0; } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 121d1049d0bc..8be9b04d8849 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -297,6 +297,8 @@ struct dsi_data { int irq; + bool is_enabled; + struct clk *dss_clk; struct clk *sys_clk; @@ -795,6 +797,9 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) dsidev = (struct platform_device *) arg; dsi = dsi_get_dsidrv_data(dsidev); + if (!dsi->is_enabled) + return IRQ_NONE; + spin_lock(&dsi->irq_lock); irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS); @@ -5671,6 +5676,15 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev) static int dsi_runtime_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); + + dsi->is_enabled = false; + /* ensure the irq handler sees the is_enabled value */ + smp_wmb(); + /* wait for current handler to finish before turning the DSI off */ + synchronize_irq(dsi->irq); + dispc_runtime_put(); return 0; @@ -5678,12 +5692,18 @@ static int dsi_runtime_suspend(struct device *dev) static int dsi_runtime_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); int r; r = dispc_runtime_get(); if (r) return r; + dsi->is_enabled = true; + /* ensure the irq handler sees the is_enabled value */ + smp_wmb(); + return 0; }