// SPDX-License-Identifier: GPL-2.0-or-later /* * twl4030-irq.c - TWL4030/TPS659x0 irq support * * Copyright (C) 2005-2006 Texas Instruments, Inc. * * Modifications to defer interrupt handling to a kernel thread: * Copyright (C) 2006 MontaVista Software, Inc. * * Based on tlv320aic23.c: * Copyright (c) by Kai Svahn <[email protected]> * * Code cleanup and modifications to IRQ handler. * by syed khasim <[email protected]> */ #include <linux/device.h> #include <linux/export.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> #include <linux/of.h> #include <linux/irqdomain.h> #include <linux/mfd/twl.h> #include "twl-core.h" /* * TWL4030 IRQ handling has two stages in hardware, and thus in software. * The Primary Interrupt Handler (PIH) stage exposes status bits saying * which Secondary Interrupt Handler (SIH) stage is raising an interrupt. * SIH modules are more traditional IRQ components, which support per-IRQ * enable/disable and trigger controls; they do most of the work. * * These chips are designed to support IRQ handling from two different * I2C masters. Each has a dedicated IRQ line, and dedicated IRQ status * and mask registers in the PIH and SIH modules. * * We set up IRQs starting at a platform-specified base, always starting * with PIH and the SIH for PWR_INT and then usually adding GPIO: * base + 0 .. base + 7 PIH * base + 8 .. base + 15 SIH for PWR_INT * base + 16 .. base + 33 SIH for GPIO */ #define TWL4030_CORE_NR_IRQS … #define TWL4030_PWR_NR_IRQS … /* PIH register offsets */ #define REG_PIH_ISR_P1 … #define REG_PIH_ISR_P2 … #define REG_PIH_SIR … /* Linux could (eventually) use either IRQ line */ static int irq_line; struct sih { … }; static const struct sih *sih_modules; static int nr_sih_modules; #define SIH_INITIALIZER(modname, nbits) … /* register naming policies are inconsistent ... */ #define TWL4030_INT_PWR_EDR … #define TWL4030_MODULE_KEYPAD_KEYP … #define TWL4030_MODULE_INT_PWR … /* * Order in this table matches order in PIH_ISR. That is, * BIT(n) in PIH_ISR is sih_modules[n]. */ /* sih_modules_twl4030 is used both in twl4030 and twl5030 */ static const struct sih sih_modules_twl4030[6] = …; static const struct sih sih_modules_twl5031[8] = …; #undef TWL4030_MODULE_KEYPAD_KEYP #undef TWL4030_MODULE_INT_PWR #undef TWL4030_INT_PWR_EDR /*----------------------------------------------------------------------*/ static unsigned twl4030_irq_base; /* * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt. * This is a chained interrupt, so there is no desc->action method for it. * Now we need to query the interrupt controller in the twl4030 to determine * which module is generating the interrupt request. However, we can't do i2c * transactions in interrupt context, so we must defer that work to a kernel * thread. All we do here is acknowledge and mask the interrupt and wakeup * the kernel thread. */ static irqreturn_t handle_twl4030_pih(int irq, void *devid) { … } /*----------------------------------------------------------------------*/ /* * twl4030_init_sih_modules() ... start from a known state where no * IRQs will be coming in, and where we can quickly enable them then * handle them as they arrive. Mask all IRQs: maybe init SIH_CTRL. * * NOTE: we don't touch EDR registers here; they stay with hardware * defaults or whatever the last value was. Note that when both EDR * bits for an IRQ are clear, that's as if its IMR bit is set... */ static int twl4030_init_sih_modules(unsigned line) { … } static inline void activate_irq(int irq) { … } /*----------------------------------------------------------------------*/ struct sih_agent { … }; /*----------------------------------------------------------------------*/ /* * All irq_chip methods get issued from code holding irq_desc[irq].lock, * which can't perform the underlying I2C operations (because they sleep). * So we must hand them off to a thread (workqueue) and cope with asynch * completion, potentially including some re-ordering, of these requests. */ static void twl4030_sih_mask(struct irq_data *data) { … } static void twl4030_sih_unmask(struct irq_data *data) { … } static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) { … } static void twl4030_sih_bus_lock(struct irq_data *data) { … } static void twl4030_sih_bus_sync_unlock(struct irq_data *data) { … } static struct irq_chip twl4030_sih_irq_chip = …; /*----------------------------------------------------------------------*/ static inline int sih_read_isr(const struct sih *sih) { … } /* * Generic handler for SIH interrupts ... we "know" this is called * in task context, with IRQs enabled. */ static irqreturn_t handle_twl4030_sih(int irq, void *data) { … } /* returns the first IRQ used by this SIH bank, or negative errno */ int twl4030_sih_setup(struct device *dev, int module, int irq_base) { … } /* FIXME need a call to reverse twl4030_sih_setup() ... */ /*----------------------------------------------------------------------*/ /* FIXME pass in which interrupt line we'll use ... */ #define twl_irq_line … int twl4030_init_irq(struct device *dev, int irq_num) { … } void twl4030_exit_irq(void) { … } int twl4030_init_chip_irq(const char *chip) { … }