Please ignore the previous version of this patch. This patch introduces seperate IRQs for the various interrupt sources of the TWL4030 power block. This is similar in spirit to the IRQs of the TWL4030 GPIO block. Signed-off-by: Peter 'p2' De Schrijver <peter.de-schrijver@xxxxxxxxx> --- drivers/i2c/chips/Kconfig | 4 + drivers/i2c/chips/Makefile | 3 +- drivers/i2c/chips/twl4030_core.c | 3 +- drivers/i2c/chips/twl4030_pwrirq.c | 230 ++++++++++++++++++++++++++++++++ include/asm-arm/arch-omap/board-rx51.h | 5 +- include/asm-arm/arch-omap/twl4030.h | 10 ++ 6 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 drivers/i2c/chips/twl4030_pwrirq.c diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f8b4e54..fa817b7 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -164,6 +164,10 @@ config TWL4030_POWEROFF bool "TWL4030 device poweroff" depends on TWL4030_CORE +config TWL4030_PWRBUTTON + bool "TWL4030 Power button Driver" + depends on TWL4030_CORE + config SENSORS_M41T00 tristate "ST M41T00 RTC chip (DEPRECATED)" depends on PPC32 diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 72a053d..94d7bd8 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -17,10 +17,11 @@ obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o obj-$(CONFIG_GPIOEXPANDER_OMAP) += gpio_expander_omap.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o -obj-$(CONFIG_TWL4030_CORE) += twl4030_core.o +obj-$(CONFIG_TWL4030_CORE) += twl4030_core.o twl4030_pwrirq.o obj-$(CONFIG_TWL4030_GPIO) += twl4030_gpio.o obj-$(CONFIG_TWL4030_USB) += twl4030_usb.o obj-$(CONFIG_TWL4030_POWEROFF) += twl4030_poweroff.o +obj-$(CONFIG_TWL4030_PWRBUTTON) += twl4030_pwrbutton.o obj-$(CONFIG_RTC_X1205_I2C) += x1205.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/chips/twl4030_pwrirq.c b/drivers/i2c/chips/twl4030_pwrirq.c new file mode 100644 index 0000000..765a6b0 --- /dev/null +++ b/drivers/i2c/chips/twl4030_pwrirq.c @@ -0,0 +1,230 @@ +/* + * twl4030_pwrirq.c - handle power interrupts from TWL3040 + * + * Copyright (C) 2008 Nokia Corporation + * + * Written by Peter De Schrijver <peter.de-schrijver@xxxxxxxxx> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel_stat.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/random.h> +#include <linux/kthread.h> +#include <asm/arch/twl4030.h> + +#define PWR_ISR1 0 +#define PWR_IMR1 1 +#define PWR_SIH_CTRL 7 +#define PWR_SIH_CTRL_COR (1<<2) + +static u8 twl4030_pwrirq_mask; +static u8 twl4030_pwrirq_pending_unmask; + +static struct task_struct *twl4030_pwrirq_unmask_thread; + +static void twl4030_pwrirq_ack(unsigned int irq) {} + +static void twl4030_pwrirq_disableint(unsigned int irq) {} + +static void twl4030_pwrirq_enableint(unsigned int irq) +{ + twl4030_pwrirq_pending_unmask |= 1 << (irq - IH_TWL4030_PWRBASE); + if (twl4030_pwrirq_unmask_thread && + twl4030_pwrirq_unmask_thread->state != TASK_RUNNING) + wake_up_process(twl4030_pwrirq_unmask_thread); +} + +static struct irq_chip twl4030_pwrirq_chip = { + .name = "twl4030-pwr", + .ack = twl4030_pwrirq_ack, + .mask = twl4030_pwrirq_disableint, + .unmask = twl4030_pwrirq_enableint, +}; + +static void do_twl4030_pwrmodule_irq(unsigned int irq, irq_desc_t *desc) +{ + struct irqaction *action; + const unsigned int cpu = smp_processor_id(); + + desc->status |= IRQ_LEVEL; + + if (!desc->depth) { + kstat_cpu(cpu).irqs[irq]++; + + action = desc->action; + if (action) { + int ret; + int status = 0; + int retval = 0; + + do { + ret = action->handler(irq, action->dev_id); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; + action = action->next; + } while (action); + + if (status & IRQF_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + + if (retval != IRQ_HANDLED) + printk(KERN_ERR "ISR for TWL4030 power module" + " irq %d can't handle interrupt\n", + irq); + } else { + local_irq_disable(); + twl4030_pwrirq_mask |= 1 << (irq - IH_TWL4030_PWRBASE); + local_irq_enable(); + twl4030_i2c_write_u8(TWL4030_MODULE_INT, + twl4030_pwrirq_mask, PWR_IMR1); + } + } +} + +static void do_twl4030_pwrirq(unsigned int irq, irq_desc_t *desc) +{ + const unsigned int cpu = smp_processor_id(); + + desc->status |= IRQ_LEVEL; + + desc->chip->ack(irq); + + if (!desc->depth) { + int ret; + int module_irq; + u8 pwr_isr; + + kstat_cpu(cpu).irqs[irq]++; + + ret = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &pwr_isr, + PWR_ISR1); + if (ret) { + printk(KERN_WARNING + "I2C error %d while reading TWL4030" + "INT PWR_ISR1 register\n", ret); + return; + } + + for (module_irq = IH_TWL4030_PWRBASE; pwr_isr != 0; + module_irq++, pwr_isr >>= 1) { + if (pwr_isr & 1) { + irq_desc_t *d = irq_desc + module_irq; + + local_irq_disable(); + + d->handle_irq(module_irq, d); + + local_irq_enable(); + } + } + + desc->chip->unmask(irq); + } +} + +static int twl4030_pwrirq_thread(void *data) +{ + current->flags |= PF_NOFREEZE; + + while (!kthread_should_stop()) { + u8 local_unmask; + + local_irq_disable(); + local_unmask = twl4030_pwrirq_pending_unmask; + twl4030_pwrirq_pending_unmask = 0; + local_irq_enable(); + + twl4030_pwrirq_mask &= ~local_unmask; + + twl4030_i2c_write_u8(TWL4030_MODULE_INT, twl4030_pwrirq_mask, + PWR_IMR1); + + local_irq_disable(); + if (!twl4030_pwrirq_pending_unmask) + set_current_state(TASK_INTERRUPTIBLE); + local_irq_enable(); + + schedule(); + } + set_current_state(TASK_RUNNING); + return 0; +} + +static int __init twl4030_pwrirq_init(void) +{ + int i, err; + u8 val; + + twl4030_pwrirq_mask = 0xff; + twl4030_pwrirq_pending_unmask = 0; + + err = twl4030_i2c_write_u8(TWL4030_MODULE_INT, twl4030_pwrirq_mask, + PWR_IMR1); + if (err) + return err; + + /* Enable clear on read */ + + err = twl4030_i2c_write_u8(TWL4030_MODULE_INT, PWR_SIH_CTRL_COR, + PWR_SIH_CTRL); + if (err) + return err; + + twl4030_pwrirq_unmask_thread = kthread_create(twl4030_pwrirq_thread, + NULL, "twl4030 pwrirq"); + if (!twl4030_pwrirq_unmask_thread) { + printk(KERN_ERR + "%s: could not create twl4030 pwrirq unmask thread!\n", + __FUNCTION__); + return -ENOMEM; + } + + for (i = IH_TWL4030_PWRBASE; i < IH_TWL4030_PWRBASE_END; i++) { + set_irq_chip(i, &twl4030_pwrirq_chip); + set_irq_handler(i, do_twl4030_pwrmodule_irq); + set_irq_flags(i, IRQF_VALID); + } + + set_irq_chained_handler(TWL4030_MODIRQ_PWR, do_twl4030_pwrirq); + + return 0; +} + +static void __exit twl4030_pwrirq_exit(void) +{ + + int i; + + set_irq_handler(TWL4030_MODIRQ_PWR, NULL); + set_irq_flags(TWL4030_MODIRQ_PWR, 0); + + for (i = IH_TWL4030_PWRBASE; i < IH_TWL4030_PWRBASE_END; i++) { + set_irq_handler(i, NULL); + set_irq_flags(i, 0); + } + + if (twl4030_pwrirq_unmask_thread) { + kthread_stop(twl4030_pwrirq_unmask_thread); + twl4030_pwrirq_unmask_thread = NULL; + } +} + +subsys_initcall(twl4030_pwrirq_init); +module_exit(twl4030_pwrirq_exit); diff --git a/include/asm-arm/arch-omap/board-rx51.h b/include/asm-arm/arch-omap/board-rx51.h index c8aa586..8fe09af 100644 --- a/include/asm-arm/arch-omap/board-rx51.h +++ b/include/asm-arm/arch-omap/board-rx51.h @@ -70,10 +70,13 @@ #define IH_TWL4030_BASE IH_BOARD_BASE #define IH_TWL4030_END (IH_TWL4030_BASE+8) +#define IH_TWL4030_PWRBASE (IH_TWL4030_END) +#define IH_TWL4030_PWRBASE_END (IH_TWL4030_PWRBASE+8) + #ifdef CONFIG_TWL4030_GPIO /* TWL4030 GPIO Interrupts */ -#define IH_TWL4030_GPIO_BASE (IH_TWL4030_END) +#define IH_TWL4030_GPIO_BASE (IH_TWL4030_PWRBASE_END) #define IH_TWL4030_GPIO_END (IH_TWL4030_BASE+18) #define NR_IRQS (IH_TWL4030_GPIO_END) #else diff --git a/include/asm-arm/arch-omap/twl4030.h b/include/asm-arm/arch-omap/twl4030.h index 7f9b90d..fc11fd0 100644 --- a/include/asm-arm/arch-omap/twl4030.h +++ b/include/asm-arm/arch-omap/twl4030.h @@ -62,6 +62,16 @@ #define TWL4030_MODIRQ_MADC (IH_TWL4030_BASE + 3) #define TWL4030_MODIRQ_USB (IH_TWL4030_BASE + 4) #define TWL4030_MODIRQ_PWR (IH_TWL4030_BASE + 5) + +#define TWL4030_PWRIRQ_PWRBTN (IH_TWL4030_PWRBASE + 0) +#define TWL4030_PWRIRQ_CHG_PRES (IH_TWL4030_PWRBASE + 1) +#define TWL4030_PWRIRQ_USB_PRES (IH_TWL4030_PWRBASE + 2) +#define TWL4030_PWRIRQ_RTC (IH_TWL4030_PWRBASE + 3) +#define TWL4030_PWRIRQ_HOT_DIE (IH_TWL4030_PWRBASE + 4) +#define TWL4030_PWRIRQ_PWROK_TIMEOUT (IH_TWL4030_PWRBASE + 5) +#define TWL4030_PWRIRQ_MBCHG (IH_TWL4030_PWRBASE + 6) +#define TWL4030_PWRIRQ_SC_DETECT (IH_TWL4030_PWRBASE + 7) + /* Rest are unsued currently*/ /* Offsets to Power Registers */ -- 1.5.3.4 -- goa is a state of mind - To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html