diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-07 15:56:04 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-07 15:56:04 -0800 |
commit | 6dc3eb5c1f96641cda7056aa34393e317076d6cf (patch) | |
tree | 9a615b884d7ff5375382b5a3f020f518f618c589 /drivers/mfd/wm8350-irq.c | |
parent | 8fe900b8c7aa6a307e552ff776e0c04c28dcf9c8 (diff) | |
parent | 2c08583c6a6b4c5f5dea4cb0931eca82af7db6fe (diff) |
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: (66 commits)
mfd: Fix ucb1x00 build failure for collie_defconfig
mfd: Fix lpc_sch related depends/selects, fix build error
gpio: Fix sch_gpio warning
gpio: add Intel SCH GPIO controller driver
i2c: convert i2c-isch to platform_device
mfd: Use completion interrupt for WM831x AUXADC
mfd: Use completion interrupt for WM835x AUXADC
mfd: Introduce remove_script function for twl4030
mfd/mmc: SDHI Kconfig update
mfd: sh_mobile_sdhi MMC_CAP_MMC_HIGHSPEED support
gpiolib: Force wm831x GPIOs into GPIO mode when requested
mfd: Add WM831x revision B support
gpiolib: Correct debugfs display of WM831x GPIO inversion
gpiolib: Actually set output state in wm831x_gpio_direction_output()
tmio_mmc: Balance cell enable()/disable() calls
tmio_mmc: Remove const from platform data V3
tmio_mmc: Use 100ms mmc_detect_change() delay
tmio_mmc: Add MMC_CAP_MMC_HIGHSPEED support V2
tmio_mmc: Keep card-detect interrupts enabled
mfd: Add twl6030 base addr for ID0, ID1, ID2
...
Diffstat (limited to 'drivers/mfd/wm8350-irq.c')
-rw-r--r-- | drivers/mfd/wm8350-irq.c | 155 |
1 files changed, 87 insertions, 68 deletions
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index 9025f29e270..f56c9adf949 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -18,7 +18,7 @@ #include <linux/bug.h> #include <linux/device.h> #include <linux/interrupt.h> -#include <linux/workqueue.h> +#include <linux/irq.h> #include <linux/mfd/wm8350/core.h> #include <linux/mfd/wm8350/audio.h> @@ -29,8 +29,6 @@ #include <linux/mfd/wm8350/supply.h> #include <linux/mfd/wm8350/wdt.h> -#define WM8350_NUM_IRQ_REGS 7 - #define WM8350_INT_OFFSET_1 0 #define WM8350_INT_OFFSET_2 1 #define WM8350_POWER_UP_INT_OFFSET 2 @@ -366,19 +364,10 @@ static struct wm8350_irq_data wm8350_irqs[] = { }, }; -static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq) +static inline struct wm8350_irq_data *irq_to_wm8350_irq(struct wm8350 *wm8350, + int irq) { - mutex_lock(&wm8350->irq_mutex); - - if (wm8350->irq[irq].handler) - wm8350->irq[irq].handler(irq, wm8350->irq[irq].data); - else { - dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n", - irq); - wm8350_mask_irq(wm8350, irq); - } - - mutex_unlock(&wm8350->irq_mutex); + return &wm8350_irqs[irq - wm8350->irq_base]; } /* @@ -386,7 +375,9 @@ static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq) * interrupts are clear on read the IRQ line will be reasserted and * the physical IRQ will be handled again if another interrupt is * asserted while we run - in the normal course of events this is a - * rare occurrence so we save I2C/SPI reads. + * rare occurrence so we save I2C/SPI reads. We're also assuming that + * it's rare to get lots of interrupts firing simultaneously so try to + * minimise I/O. */ static irqreturn_t wm8350_irq(int irq, void *irq_data) { @@ -397,7 +388,6 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data) struct wm8350_irq_data *data; int i; - /* TODO: Use block reads to improve performance? */ level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS) & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK); @@ -416,93 +406,101 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data) sub_reg[data->reg] = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 + data->reg); - sub_reg[data->reg] &= - ~wm8350_reg_read(wm8350, - WM8350_INT_STATUS_1_MASK + - data->reg); + sub_reg[data->reg] &= ~wm8350->irq_masks[data->reg]; read_done[data->reg] = 1; } if (sub_reg[data->reg] & data->mask) - wm8350_irq_call_handler(wm8350, i); + handle_nested_irq(wm8350->irq_base + i); } return IRQ_HANDLED; } -int wm8350_register_irq(struct wm8350 *wm8350, int irq, - irq_handler_t handler, unsigned long flags, - const char *name, void *data) +static void wm8350_irq_lock(unsigned int irq) { - if (irq < 0 || irq >= WM8350_NUM_IRQ || !handler) - return -EINVAL; - - if (wm8350->irq[irq].handler) - return -EBUSY; - - mutex_lock(&wm8350->irq_mutex); - wm8350->irq[irq].handler = handler; - wm8350->irq[irq].data = data; - mutex_unlock(&wm8350->irq_mutex); - - wm8350_unmask_irq(wm8350, irq); + struct wm8350 *wm8350 = get_irq_chip_data(irq); - return 0; + mutex_lock(&wm8350->irq_lock); } -EXPORT_SYMBOL_GPL(wm8350_register_irq); -int wm8350_free_irq(struct wm8350 *wm8350, int irq) +static void wm8350_irq_sync_unlock(unsigned int irq) { - if (irq < 0 || irq >= WM8350_NUM_IRQ) - return -EINVAL; + struct wm8350 *wm8350 = get_irq_chip_data(irq); + int i; - wm8350_mask_irq(wm8350, irq); + for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) { + /* If there's been a change in the mask write it back + * to the hardware. */ + if (wm8350->irq_masks[i] != + wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i]) + WARN_ON(wm8350_reg_write(wm8350, + WM8350_INT_STATUS_1_MASK + i, + wm8350->irq_masks[i])); + } - mutex_lock(&wm8350->irq_mutex); - wm8350->irq[irq].handler = NULL; - mutex_unlock(&wm8350->irq_mutex); - return 0; + mutex_unlock(&wm8350->irq_lock); } -EXPORT_SYMBOL_GPL(wm8350_free_irq); -int wm8350_mask_irq(struct wm8350 *wm8350, int irq) +static void wm8350_irq_enable(unsigned int irq) { - return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK + - wm8350_irqs[irq].reg, - wm8350_irqs[irq].mask); + struct wm8350 *wm8350 = get_irq_chip_data(irq); + struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq); + + wm8350->irq_masks[irq_data->reg] &= ~irq_data->mask; } -EXPORT_SYMBOL_GPL(wm8350_mask_irq); -int wm8350_unmask_irq(struct wm8350 *wm8350, int irq) +static void wm8350_irq_disable(unsigned int irq) { - return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK + - wm8350_irqs[irq].reg, - wm8350_irqs[irq].mask); + struct wm8350 *wm8350 = get_irq_chip_data(irq); + struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq); + + wm8350->irq_masks[irq_data->reg] |= irq_data->mask; } -EXPORT_SYMBOL_GPL(wm8350_unmask_irq); + +static struct irq_chip wm8350_irq_chip = { + .name = "wm8350", + .bus_lock = wm8350_irq_lock, + .bus_sync_unlock = wm8350_irq_sync_unlock, + .disable = wm8350_irq_disable, + .enable = wm8350_irq_enable, +}; int wm8350_irq_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { - int ret; + int ret, cur_irq, i; int flags = IRQF_ONESHOT; if (!irq) { - dev_err(wm8350->dev, "No IRQ configured\n"); - return -EINVAL; + dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n"); + return 0; + } + + if (!pdata || !pdata->irq_base) { + dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n"); + return 0; } + /* Mask top level interrupts */ wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF); - wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF); - wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF); - wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF); - wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF); - wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF); - mutex_init(&wm8350->irq_mutex); + /* Mask all individual interrupts by default and cache the + * masks. We read the masks back since there are unwritable + * bits in the mask registers. */ + for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) { + wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK + i, + 0xFFFF); + wm8350->irq_masks[i] = + wm8350_reg_read(wm8350, + WM8350_INT_STATUS_1_MASK + i); + } + + mutex_init(&wm8350->irq_lock); wm8350->chip_irq = irq; + wm8350->irq_base = pdata->irq_base; - if (pdata && pdata->irq_high) { + if (pdata->irq_high) { flags |= IRQF_TRIGGER_HIGH; wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1, @@ -514,11 +512,32 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq, WM8350_IRQ_POL); } + /* Register with genirq */ + for (cur_irq = wm8350->irq_base; + cur_irq < ARRAY_SIZE(wm8350_irqs) + wm8350->irq_base; + cur_irq++) { + set_irq_chip_data(cur_irq, wm8350); + set_irq_chip_and_handler(cur_irq, &wm8350_irq_chip, + handle_edge_irq); + set_irq_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + set_irq_noprobe(cur_irq); +#endif + } + ret = request_threaded_irq(irq, NULL, wm8350_irq, flags, "wm8350", wm8350); if (ret != 0) dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret); + /* Allow interrupts to fire */ + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0); + return ret; } |