diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-31 14:31:44 -1000 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-31 14:31:44 -1000 |
commit | 3da3f872aa175f59e20766ed30aaea67fd4fa7d1 (patch) | |
tree | a44697edf5eebd02b6f159a7ab824a45730134b4 /drivers/mfd/wm831x-irq.c | |
parent | 968e75fc13b6d582f42ce44172e13ba58157e11f (diff) | |
parent | e178ccb33569da17dc897a08a3865441b813bdfb (diff) | |
download | op-kernel-dev-3da3f872aa175f59e20766ed30aaea67fd4fa7d1.zip op-kernel-dev-3da3f872aa175f59e20766ed30aaea67fd4fa7d1.tar.gz |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (46 commits)
mfd: Fix mismatch in twl4030 mutex lock-unlock
mfd: twl6030-pwm.c needs MODULE_LICENSE
mfd: Fix the omap-usb-host clock API usage on usbhs_disable()
mfd: Acknowledge WM8994 IRQs before reporting
mfd: Acknowlege all WM831x IRQs before we handle them
mfd: Avoid two assignments if failures happen in tps65910_i2c_probe
regulator: Storing tps65912 error codes in u8
mfd: Don't leak init_data in tps65910_i2c_probe
regulator: aat2870: Add AAT2870 regulator driver
backlight: Add AAT2870 backlight driver
mfd: Add AAT2870 mfd driver
mfd: Remove dead code from max8997-irq
mfd: Move TPS55910 Kconfig option
mfd: Fix missing stmpe kerneldoc
mfd: Fix off-by-one value range checking for tps65912_i2c_write
mfd: Add devices for WM831x clocking module
mfd: Remove comp{1,2}_threshold sysfs entries in tps65911_comparator_remove
mfd: Don't ask about the TPS65912 core driver in Kconfig
mfd: Fix off by one in WM831x IRQ code
mfd: Add tps65921 support from twl-core
...
Diffstat (limited to 'drivers/mfd/wm831x-irq.c')
-rw-r--r-- | drivers/mfd/wm831x-irq.c | 77 |
1 files changed, 49 insertions, 28 deletions
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 42b928e..ada1835 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,30 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } + /* Rebase the IRQ into the GPIO range so we've got a sensible array + * index. + */ + irq -= WM831X_IRQ_GPIO_1; + + /* 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 = { @@ -432,7 +449,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) { struct wm831x *wm831x = data; unsigned int i; - int primary; + int primary, status_addr; int status_regs[WM831X_NUM_IRQ_REGS] = { 0 }; int read[WM831X_NUM_IRQ_REGS] = { 0 }; int *status; @@ -467,8 +484,9 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) /* Hopefully there should only be one register to read * each time otherwise we ought to do a block read. */ if (!read[offset]) { - *status = wm831x_reg_read(wm831x, - irq_data_to_status_reg(&wm831x_irqs[i])); + status_addr = irq_data_to_status_reg(&wm831x_irqs[i]); + + *status = wm831x_reg_read(wm831x, status_addr); if (*status < 0) { dev_err(wm831x->dev, "Failed to read IRQ status: %d\n", @@ -477,26 +495,21 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) } read[offset] = 1; + + /* Ignore any bits that we don't think are masked */ + *status &= ~wm831x->irq_masks_cur[offset]; + + /* Acknowledge now so we don't miss + * notifications while we handle. + */ + wm831x_reg_write(wm831x, status_addr, *status); } - /* Report it if it isn't masked, or forget the status. */ - if ((*status & ~wm831x->irq_masks_cur[offset]) - & wm831x_irqs[i].mask) + if (*status & wm831x_irqs[i].mask) handle_nested_irq(wm831x->irq_base + i); - else - *status &= ~wm831x_irqs[i].mask; } out: - /* Touchscreen interrupts are handled specially in the driver */ - status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT); - - for (i = 0; i < ARRAY_SIZE(status_regs); i++) { - if (status_regs[i]) - wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i, - status_regs[i]); - } - return IRQ_HANDLED; } @@ -515,13 +528,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) 0xffff); } - if (!pdata || !pdata->irq_base) { - dev_err(wm831x->dev, - "No interrupt base specified, no interrupts\n"); + /* Try to dynamically allocate IRQs if no base is specified */ + if (!pdata || !pdata->irq_base) + wm831x->irq_base = -1; + else + wm831x->irq_base = pdata->irq_base; + + wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0, + WM831X_NUM_IRQS, 0); + if (wm831x->irq_base < 0) { + dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n", + wm831x->irq_base); + wm831x->irq_base = 0; return 0; } - if (pdata->irq_cmos) + if (pdata && pdata->irq_cmos) i = 0; else i = WM831X_IRQ_OD; @@ -541,7 +563,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) } wm831x->irq = irq; - wm831x->irq_base = pdata->irq_base; /* Register them with genirq */ for (cur_irq = wm831x->irq_base; |