Input: wm831x-ts - fix races with IRQ management

If the WM831x pen down and data IRQs run in parallel it is possible for the
data and pen down IRQs to deadlock themselves as one is part way through
disabling its operation while the other is part way through enabling. Fix
this by always disabling the pen down interrupt while data is active and
vice versa. When a changeover is required we disable the IRQ that is to
be stopped then schedule work that will enable the new IRQ.

We need to handle the data flow in the data IRQ as the readback from the
device needs to be ordered correctly with the IRQ for robust operation.

This also fixes an issue when using the built in IRQs due to enable_irq()
not being valid from interrupt context on an interrupt controller with bus
operations like the built in IRQ controller - this issue may also have
affected other interrupt controllers. We can't rely on having the data
and pen down IRQs available via GPIOs on the CPU on every system.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by Mark Brown and committed by Dmitry Torokhov f5346668 c36b58e8

+49 -5
+49 -5
drivers/input/touchscreen/wm831x-ts.c
··· 68 68 unsigned int pd_irq; 69 69 bool pressure; 70 70 bool pen_down; 71 + struct work_struct pd_data_work; 71 72 }; 73 + 74 + static void wm831x_pd_data_work(struct work_struct *work) 75 + { 76 + struct wm831x_ts *wm831x_ts = 77 + container_of(work, struct wm831x_ts, pd_data_work); 78 + 79 + if (wm831x_ts->pen_down) { 80 + enable_irq(wm831x_ts->data_irq); 81 + dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n"); 82 + } else { 83 + enable_irq(wm831x_ts->pd_irq); 84 + dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n"); 85 + } 86 + } 72 87 73 88 static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) 74 89 { ··· 125 110 } 126 111 127 112 if (!wm831x_ts->pen_down) { 113 + /* Switch from data to pen down */ 114 + dev_dbg(wm831x->dev, "IRQ DATA->PD\n"); 115 + 128 116 disable_irq_nosync(wm831x_ts->data_irq); 129 117 130 118 /* Don't need data any more */ ··· 146 128 ABS_PRESSURE, 0); 147 129 148 130 input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); 131 + 132 + schedule_work(&wm831x_ts->pd_data_work); 149 133 } 150 134 151 135 input_sync(wm831x_ts->input_dev); ··· 160 140 struct wm831x_ts *wm831x_ts = irq_data; 161 141 struct wm831x *wm831x = wm831x_ts->wm831x; 162 142 int ena = 0; 143 + 144 + if (wm831x_ts->pen_down) 145 + return IRQ_HANDLED; 146 + 147 + disable_irq_nosync(wm831x_ts->pd_irq); 163 148 164 149 /* Start collecting data */ 165 150 if (wm831x_ts->pressure) ··· 181 156 WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); 182 157 183 158 wm831x_ts->pen_down = true; 184 - enable_irq(wm831x_ts->data_irq); 159 + 160 + /* Switch from pen down to data */ 161 + dev_dbg(wm831x->dev, "IRQ PD->DATA\n"); 162 + schedule_work(&wm831x_ts->pd_data_work); 185 163 186 164 return IRQ_HANDLED; 187 165 } ··· 210 182 struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); 211 183 struct wm831x *wm831x = wm831x_ts->wm831x; 212 184 185 + /* Shut the controller down, disabling all other functionality too */ 213 186 wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, 214 - WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | 215 - WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | 216 - WM831X_TCH_Z_ENA, 0); 187 + WM831X_TCH_ENA | WM831X_TCH_X_ENA | 188 + WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); 217 189 218 - if (wm831x_ts->pen_down) 190 + /* Make sure any pending IRQs are done, the above will prevent 191 + * new ones firing. 192 + */ 193 + synchronize_irq(wm831x_ts->data_irq); 194 + synchronize_irq(wm831x_ts->pd_irq); 195 + 196 + /* Make sure the IRQ completion work is quiesced */ 197 + flush_work_sync(&wm831x_ts->pd_data_work); 198 + 199 + /* If we ended up with the pen down then make sure we revert back 200 + * to pen detection state for the next time we start up. 201 + */ 202 + if (wm831x_ts->pen_down) { 219 203 disable_irq(wm831x_ts->data_irq); 204 + enable_irq(wm831x_ts->pd_irq); 205 + wm831x_ts->pen_down = false; 206 + } 220 207 } 221 208 222 209 static __devinit int wm831x_ts_probe(struct platform_device *pdev) ··· 255 212 256 213 wm831x_ts->wm831x = wm831x; 257 214 wm831x_ts->input_dev = input_dev; 215 + INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work); 258 216 259 217 /* 260 218 * If we have a direct IRQ use it, otherwise use the interrupt