From patchwork Mon Mar 14 22:45:01 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Brown X-Patchwork-Id: 634761 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p2EMilgO020412 for ; Mon, 14 Mar 2011 22:44:48 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756698Ab1CNWor (ORCPT ); Mon, 14 Mar 2011 18:44:47 -0400 Received: from opensource.wolfsonmicro.com ([80.75.67.52]:35947 "EHLO opensource2.wolfsonmicro.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751715Ab1CNWoq (ORCPT ); Mon, 14 Mar 2011 18:44:46 -0400 Received: from finisterre.wolfsonmicro.main (unknown [87.246.78.26]) by opensource2.wolfsonmicro.com (Postfix) with ESMTPSA id 066BA1100F2; Mon, 14 Mar 2011 22:44:45 +0000 (GMT) Received: from broonie by finisterre.wolfsonmicro.main with local (Exim 4.74) (envelope-from ) id 1PzGVa-0005nd-KX; Mon, 14 Mar 2011 22:45:02 +0000 From: Mark Brown To: Dmitry Torokhov Cc: linux-input@vger.kernel.org, patches@opensource.wolfsonmicro.com, Mark Brown Subject: [PATCH 2/2] Input: wm831x-ts - Fix races with IRQ management Date: Mon, 14 Mar 2011 22:45:01 +0000 Message-Id: <1300142701-22260-2-git-send-email-broonie@opensource.wolfsonmicro.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1300142701-22260-1-git-send-email-broonie@opensource.wolfsonmicro.com> References: <1300142701-22260-1-git-send-email-broonie@opensource.wolfsonmicro.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Mon, 14 Mar 2011 22:44:48 +0000 (UTC) diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c index 6ae054f..3f825cb 100644 --- a/drivers/input/touchscreen/wm831x-ts.c +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -68,8 +68,29 @@ struct wm831x_ts { unsigned int pd_irq; bool pressure; bool pen_down; + struct work_struct pd_data_work; + struct work_struct data_pd_work; }; +static void wm831x_pd_data(struct work_struct *work) +{ + struct wm831x_ts *wm831x_ts = + container_of(work, struct wm831x_ts, pd_data_work); + + disable_irq(wm831x_ts->pd_irq); + enable_irq(wm831x_ts->data_irq); + dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n"); +} + +static void wm831x_data_pd(struct work_struct *work) +{ + struct wm831x_ts *wm831x_ts = + container_of(work, struct wm831x_ts, data_pd_work); + + enable_irq(wm831x_ts->pd_irq); + dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n"); +} + static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) { struct wm831x_ts *wm831x_ts = irq_data; @@ -110,7 +131,10 @@ static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) } if (!wm831x_ts->pen_down) { + /* Switch from data to pen down */ + dev_dbg(wm831x->dev, "IRQ DATA->PD\n"); disable_irq_nosync(wm831x_ts->data_irq); + schedule_work(&wm831x_ts->data_pd_work); /* Don't need data any more */ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, @@ -156,7 +180,10 @@ static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); wm831x_ts->pen_down = true; - enable_irq(wm831x_ts->data_irq); + + /* Switch from pen down to data */ + dev_dbg(wm831x->dev, "IRQ PD->DATA\n"); + schedule_work(&wm831x_ts->pd_data_work); return IRQ_HANDLED; } @@ -182,13 +209,36 @@ static void wm831x_ts_input_close(struct input_dev *idev) struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); struct wm831x *wm831x = wm831x_ts->wm831x; + /* Shut the controller down, disabling all other functionality */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA, 0); + + /* Make sure any pending IRQs are done, the above will prevent + * new ones firing. + */ + synchronize_irq(wm831x_ts->data_irq); + synchronize_irq(wm831x_ts->pd_irq); + + /* Make sure the IRQ completion work is quiesced */ + flush_work_sync(&wm831x_ts->data_pd_work); + flush_work_sync(&wm831x_ts->pd_data_work); + + /* If we ended up with the pen down then make sure we revert back + * to pen detection state for the next time we start up. + */ + if (wm831x_ts->pen_down) { + disable_irq(wm831x_ts->data_irq); + enable_irq(wm831x_ts->pd_irq); + wm831x_ts->pen_down = false; + } + + /* Be nice to the next user and make sure everything is + * flagged as disabled. + */ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); - - if (wm831x_ts->pen_down) - disable_irq(wm831x_ts->data_irq); } static __devinit int wm831x_ts_probe(struct platform_device *pdev) @@ -212,6 +262,8 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev) wm831x_ts->wm831x = wm831x; wm831x_ts->input_dev = input_dev; + INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data); + INIT_WORK(&wm831x_ts->data_pd_work, wm831x_data_pd); /* * If we have a direct IRQ use it, otherwise use the interrupt