diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-06-02 19:18:47 +0100 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-07-31 23:28:19 +0200 |
commit | ca7a71824ac957b1b9d3322656c05aad38d7275c (patch) | |
tree | ff89c76b4cda114343c01f332aa28fb80095f3db /drivers/mfd | |
parent | 24c3047095fa3954f114bfff2e37b8fcbb216396 (diff) | |
download | op-kernel-dev-ca7a71824ac957b1b9d3322656c05aad38d7275c.zip op-kernel-dev-ca7a71824ac957b1b9d3322656c05aad38d7275c.tar.gz |
mfd: Fix bus lock interaction for WM831x IRQ set_type() operation
The WM831x IRQ set_type() operation is doing a direct register write when
called but since set_type() is called with the bus lock held this isn't
legal and could cause deadlocks in the IRQ core.
Fix this by posting the updates into an array and syncing in the
bus_sync_unlock() callback.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/wm831x-irq.c | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 42b928e..b23d8d5 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -348,6 +348,15 @@ static void wm831x_irq_sync_unlock(struct irq_data *data) struct wm831x *wm831x = irq_data_get_irq_chip_data(data); int i; + for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) { + if (wm831x->gpio_update[i]) { + wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i, + WM831X_GPN_INT_MODE | WM831X_GPN_POL, + wm831x->gpio_update[i]); + wm831x->gpio_update[i] = 0; + } + } + for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { /* If there's been a change in the mask write it back * to the hardware. */ @@ -387,7 +396,7 @@ static void wm831x_irq_disable(struct irq_data *data) static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); - int val, irq; + int irq; irq = data->irq - wm831x->irq_base; @@ -399,22 +408,25 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } + /* We set the high bit to flag that we need an update; don't + * do the update here as we can be called with the bus lock + * held. + */ switch (type) { case IRQ_TYPE_EDGE_BOTH: - val = WM831X_GPN_INT_MODE; + wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; break; case IRQ_TYPE_EDGE_RISING: - val = WM831X_GPN_POL; + wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; break; case IRQ_TYPE_EDGE_FALLING: - val = 0; + wm831x->gpio_update[irq] = 0x10000; break; default: return -EINVAL; } - return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq, - WM831X_GPN_INT_MODE | WM831X_GPN_POL, val); + return 0; } static struct irq_chip wm831x_irq_chip = { |