xref: /openbmc/linux/drivers/mfd/twl6030-irq.c (revision 72f2e2c7)
1e8deb28cSBalaji T K /*
2e8deb28cSBalaji T K  * twl6030-irq.c - TWL6030 irq support
3e8deb28cSBalaji T K  *
4e8deb28cSBalaji T K  * Copyright (C) 2005-2009 Texas Instruments, Inc.
5e8deb28cSBalaji T K  *
6e8deb28cSBalaji T K  * Modifications to defer interrupt handling to a kernel thread:
7e8deb28cSBalaji T K  * Copyright (C) 2006 MontaVista Software, Inc.
8e8deb28cSBalaji T K  *
9e8deb28cSBalaji T K  * Based on tlv320aic23.c:
10e8deb28cSBalaji T K  * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
11e8deb28cSBalaji T K  *
12e8deb28cSBalaji T K  * Code cleanup and modifications to IRQ handler.
13e8deb28cSBalaji T K  * by syed khasim <x0khasim@ti.com>
14e8deb28cSBalaji T K  *
15e8deb28cSBalaji T K  * TWL6030 specific code and IRQ handling changes by
16e8deb28cSBalaji T K  * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
17e8deb28cSBalaji T K  * Balaji T K <balajitk@ti.com>
18e8deb28cSBalaji T K  *
19e8deb28cSBalaji T K  * This program is free software; you can redistribute it and/or modify
20e8deb28cSBalaji T K  * it under the terms of the GNU General Public License as published by
21e8deb28cSBalaji T K  * the Free Software Foundation; either version 2 of the License, or
22e8deb28cSBalaji T K  * (at your option) any later version.
23e8deb28cSBalaji T K  *
24e8deb28cSBalaji T K  * This program is distributed in the hope that it will be useful,
25e8deb28cSBalaji T K  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26e8deb28cSBalaji T K  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27e8deb28cSBalaji T K  * GNU General Public License for more details.
28e8deb28cSBalaji T K  *
29e8deb28cSBalaji T K  * You should have received a copy of the GNU General Public License
30e8deb28cSBalaji T K  * along with this program; if not, write to the Free Software
31e8deb28cSBalaji T K  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
32e8deb28cSBalaji T K  */
33e8deb28cSBalaji T K 
34e8deb28cSBalaji T K #include <linux/init.h>
35e8deb28cSBalaji T K #include <linux/interrupt.h>
36e8deb28cSBalaji T K #include <linux/irq.h>
37e8deb28cSBalaji T K #include <linux/kthread.h>
38e8deb28cSBalaji T K #include <linux/i2c/twl.h>
3972f2e2c7Skishore kadiyala #include <linux/platform_device.h>
40e8deb28cSBalaji T K 
41e8deb28cSBalaji T K /*
42e8deb28cSBalaji T K  * TWL6030 (unlike its predecessors, which had two level interrupt handling)
43e8deb28cSBalaji T K  * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
44e8deb28cSBalaji T K  * It exposes status bits saying who has raised an interrupt. There are
45e8deb28cSBalaji T K  * three mask registers that corresponds to these status registers, that
46e8deb28cSBalaji T K  * enables/disables these interrupts.
47e8deb28cSBalaji T K  *
48e8deb28cSBalaji T K  * We set up IRQs starting at a platform-specified base. An interrupt map table,
49e8deb28cSBalaji T K  * specifies mapping between interrupt number and the associated module.
50e8deb28cSBalaji T K  *
51e8deb28cSBalaji T K  */
52e8deb28cSBalaji T K 
53e8deb28cSBalaji T K static int twl6030_interrupt_mapping[24] = {
54e8deb28cSBalaji T K 	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/
55e8deb28cSBalaji T K 	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/
56e8deb28cSBalaji T K 	PWR_INTR_OFFSET,	/* Bit 2	BAT_VLOW		*/
57e8deb28cSBalaji T K 	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/
58e8deb28cSBalaji T K 	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/
59e8deb28cSBalaji T K 	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/
60e8deb28cSBalaji T K 	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/
61e8deb28cSBalaji T K 	SMPSLDO_INTR_OFFSET,	/* Bit 7	VMMC_SHORT		*/
62e8deb28cSBalaji T K 
63e8deb28cSBalaji T K 	SMPSLDO_INTR_OFFSET,	/* Bit 8	VUSIM_SHORT		*/
64e8deb28cSBalaji T K 	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/
65e8deb28cSBalaji T K 	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/
66e8deb28cSBalaji T K 	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/
67e8deb28cSBalaji T K 	RSV_INTR_OFFSET,  	/* Bit 12	Reserved		*/
68e8deb28cSBalaji T K 	MADC_INTR_OFFSET,	/* Bit 13	GPADC_RT_EOC		*/
69e8deb28cSBalaji T K 	MADC_INTR_OFFSET,	/* Bit 14	GPADC_SW_EOC		*/
70e8deb28cSBalaji T K 	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/
71e8deb28cSBalaji T K 
72e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/
73e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/
74e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/
75e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 19	VBUS			*/
76e8deb28cSBalaji T K 	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/
77e8deb28cSBalaji T K 	CHARGER_INTR_OFFSET,	/* Bit 21	EXT_CHRG		*/
78e8deb28cSBalaji T K 	CHARGER_INTR_OFFSET,	/* Bit 22	INT_CHRG		*/
79e8deb28cSBalaji T K 	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/
80e8deb28cSBalaji T K };
81e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
82e8deb28cSBalaji T K 
83e8deb28cSBalaji T K static unsigned twl6030_irq_base;
84e8deb28cSBalaji T K 
85e8deb28cSBalaji T K static struct completion irq_event;
86e8deb28cSBalaji T K 
87e8deb28cSBalaji T K /*
88e8deb28cSBalaji T K  * This thread processes interrupts reported by the Primary Interrupt Handler.
89e8deb28cSBalaji T K  */
90e8deb28cSBalaji T K static int twl6030_irq_thread(void *data)
91e8deb28cSBalaji T K {
92e8deb28cSBalaji T K 	long irq = (long)data;
93e8deb28cSBalaji T K 	static unsigned i2c_errors;
94e8deb28cSBalaji T K 	static const unsigned max_i2c_errors = 100;
95e8deb28cSBalaji T K 	int ret;
96e8deb28cSBalaji T K 
97e8deb28cSBalaji T K 	current->flags |= PF_NOFREEZE;
98e8deb28cSBalaji T K 
99e8deb28cSBalaji T K 	while (!kthread_should_stop()) {
100e8deb28cSBalaji T K 		int i;
101e8deb28cSBalaji T K 		union {
102e8deb28cSBalaji T K 		u8 bytes[4];
103e8deb28cSBalaji T K 		u32 int_sts;
104e8deb28cSBalaji T K 		} sts;
105e8deb28cSBalaji T K 
106e8deb28cSBalaji T K 		/* Wait for IRQ, then read PIH irq status (also blocking) */
107e8deb28cSBalaji T K 		wait_for_completion_interruptible(&irq_event);
108e8deb28cSBalaji T K 
109e8deb28cSBalaji T K 		/* read INT_STS_A, B and C in one shot using a burst read */
110e8deb28cSBalaji T K 		ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
111e8deb28cSBalaji T K 				REG_INT_STS_A, 3);
112e8deb28cSBalaji T K 		if (ret) {
113e8deb28cSBalaji T K 			pr_warning("twl6030: I2C error %d reading PIH ISR\n",
114e8deb28cSBalaji T K 					ret);
115e8deb28cSBalaji T K 			if (++i2c_errors >= max_i2c_errors) {
116e8deb28cSBalaji T K 				printk(KERN_ERR "Maximum I2C error count"
117e8deb28cSBalaji T K 						" exceeded.  Terminating %s.\n",
118e8deb28cSBalaji T K 						__func__);
119e8deb28cSBalaji T K 				break;
120e8deb28cSBalaji T K 			}
121e8deb28cSBalaji T K 			complete(&irq_event);
122e8deb28cSBalaji T K 			continue;
123e8deb28cSBalaji T K 		}
124e8deb28cSBalaji T K 
125e8deb28cSBalaji T K 
126e8deb28cSBalaji T K 
127e8deb28cSBalaji T K 		sts.bytes[3] = 0; /* Only 24 bits are valid*/
128e8deb28cSBalaji T K 
129e8deb28cSBalaji T K 		for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
130e8deb28cSBalaji T K 			local_irq_disable();
131e8deb28cSBalaji T K 			if (sts.int_sts & 0x1) {
132e8deb28cSBalaji T K 				int module_irq = twl6030_irq_base +
133e8deb28cSBalaji T K 					twl6030_interrupt_mapping[i];
134e8deb28cSBalaji T K 				struct irq_desc *d = irq_to_desc(module_irq);
135e8deb28cSBalaji T K 
136e8deb28cSBalaji T K 				if (!d) {
137e8deb28cSBalaji T K 					pr_err("twl6030: Invalid SIH IRQ: %d\n",
138e8deb28cSBalaji T K 					       module_irq);
139e8deb28cSBalaji T K 					return -EINVAL;
140e8deb28cSBalaji T K 				}
141e8deb28cSBalaji T K 
142e8deb28cSBalaji T K 				/* These can't be masked ... always warn
143e8deb28cSBalaji T K 				 * if we get any surprises.
144e8deb28cSBalaji T K 				 */
145e8deb28cSBalaji T K 				if (d->status & IRQ_DISABLED)
146e8deb28cSBalaji T K 					note_interrupt(module_irq, d,
147e8deb28cSBalaji T K 							IRQ_NONE);
148e8deb28cSBalaji T K 				else
149e8deb28cSBalaji T K 					d->handle_irq(module_irq, d);
150e8deb28cSBalaji T K 
151e8deb28cSBalaji T K 			}
152e8deb28cSBalaji T K 		local_irq_enable();
153e8deb28cSBalaji T K 		}
154e8deb28cSBalaji T K 		ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
155e8deb28cSBalaji T K 				REG_INT_STS_A, 3); /* clear INT_STS_A */
156e8deb28cSBalaji T K 		if (ret)
157e8deb28cSBalaji T K 			pr_warning("twl6030: I2C error in clearing PIH ISR\n");
158e8deb28cSBalaji T K 
159e8deb28cSBalaji T K 		enable_irq(irq);
160e8deb28cSBalaji T K 	}
161e8deb28cSBalaji T K 
162e8deb28cSBalaji T K 	return 0;
163e8deb28cSBalaji T K }
164e8deb28cSBalaji T K 
165e8deb28cSBalaji T K /*
166e8deb28cSBalaji T K  * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
167e8deb28cSBalaji T K  * This is a chained interrupt, so there is no desc->action method for it.
168e8deb28cSBalaji T K  * Now we need to query the interrupt controller in the twl6030 to determine
169e8deb28cSBalaji T K  * which module is generating the interrupt request.  However, we can't do i2c
170e8deb28cSBalaji T K  * transactions in interrupt context, so we must defer that work to a kernel
171e8deb28cSBalaji T K  * thread.  All we do here is acknowledge and mask the interrupt and wakeup
172e8deb28cSBalaji T K  * the kernel thread.
173e8deb28cSBalaji T K  */
174e8deb28cSBalaji T K static irqreturn_t handle_twl6030_pih(int irq, void *devid)
175e8deb28cSBalaji T K {
176e8deb28cSBalaji T K 	disable_irq_nosync(irq);
177e8deb28cSBalaji T K 	complete(devid);
178e8deb28cSBalaji T K 	return IRQ_HANDLED;
179e8deb28cSBalaji T K }
180e8deb28cSBalaji T K 
181e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
182e8deb28cSBalaji T K 
183e8deb28cSBalaji T K static inline void activate_irq(int irq)
184e8deb28cSBalaji T K {
185e8deb28cSBalaji T K #ifdef CONFIG_ARM
186e8deb28cSBalaji T K 	/* ARM requires an extra step to clear IRQ_NOREQUEST, which it
187e8deb28cSBalaji T K 	 * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
188e8deb28cSBalaji T K 	 */
189e8deb28cSBalaji T K 	set_irq_flags(irq, IRQF_VALID);
190e8deb28cSBalaji T K #else
191e8deb28cSBalaji T K 	/* same effect on other architectures */
192e8deb28cSBalaji T K 	set_irq_noprobe(irq);
193e8deb28cSBalaji T K #endif
194e8deb28cSBalaji T K }
195e8deb28cSBalaji T K 
196e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
197e8deb28cSBalaji T K 
198e8deb28cSBalaji T K static unsigned twl6030_irq_next;
199e8deb28cSBalaji T K 
200e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
201e8deb28cSBalaji T K int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
202e8deb28cSBalaji T K {
203e8deb28cSBalaji T K 	int ret;
204e8deb28cSBalaji T K 	u8 unmask_value;
205e8deb28cSBalaji T K 	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
206e8deb28cSBalaji T K 			REG_INT_STS_A + offset);
207e8deb28cSBalaji T K 	unmask_value &= (~(bit_mask));
208e8deb28cSBalaji T K 	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
209e8deb28cSBalaji T K 			REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
210e8deb28cSBalaji T K 	return ret;
211e8deb28cSBalaji T K }
212e8deb28cSBalaji T K EXPORT_SYMBOL(twl6030_interrupt_unmask);
213e8deb28cSBalaji T K 
214e8deb28cSBalaji T K int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
215e8deb28cSBalaji T K {
216e8deb28cSBalaji T K 	int ret;
217e8deb28cSBalaji T K 	u8 mask_value;
218e8deb28cSBalaji T K 	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
219e8deb28cSBalaji T K 			REG_INT_STS_A + offset);
220e8deb28cSBalaji T K 	mask_value |= (bit_mask);
221e8deb28cSBalaji T K 	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
222e8deb28cSBalaji T K 			REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
223e8deb28cSBalaji T K 	return ret;
224e8deb28cSBalaji T K }
225e8deb28cSBalaji T K EXPORT_SYMBOL(twl6030_interrupt_mask);
226e8deb28cSBalaji T K 
22772f2e2c7Skishore kadiyala int twl6030_mmc_card_detect_config(void)
22872f2e2c7Skishore kadiyala {
22972f2e2c7Skishore kadiyala 	int ret;
23072f2e2c7Skishore kadiyala 	u8 reg_val = 0;
23172f2e2c7Skishore kadiyala 
23272f2e2c7Skishore kadiyala 	/* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
23372f2e2c7Skishore kadiyala 	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
23472f2e2c7Skishore kadiyala 						REG_INT_MSK_LINE_B);
23572f2e2c7Skishore kadiyala 	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
23672f2e2c7Skishore kadiyala 						REG_INT_MSK_STS_B);
23772f2e2c7Skishore kadiyala 	/*
23872f2e2c7Skishore kadiyala 	 * Intially Configuring MMC_CTRL for receving interrupts &
23972f2e2c7Skishore kadiyala 	 * Card status on TWL6030 for MMC1
24072f2e2c7Skishore kadiyala 	 */
24172f2e2c7Skishore kadiyala 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
24272f2e2c7Skishore kadiyala 	if (ret < 0) {
24372f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
24472f2e2c7Skishore kadiyala 		return ret;
24572f2e2c7Skishore kadiyala 	}
24672f2e2c7Skishore kadiyala 	reg_val &= ~VMMC_AUTO_OFF;
24772f2e2c7Skishore kadiyala 	reg_val |= SW_FC;
24872f2e2c7Skishore kadiyala 	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
24972f2e2c7Skishore kadiyala 	if (ret < 0) {
25072f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
25172f2e2c7Skishore kadiyala 		return ret;
25272f2e2c7Skishore kadiyala 	}
25372f2e2c7Skishore kadiyala 
25472f2e2c7Skishore kadiyala 	/* Configuring PullUp-PullDown register */
25572f2e2c7Skishore kadiyala 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
25672f2e2c7Skishore kadiyala 						TWL6030_CFG_INPUT_PUPD3);
25772f2e2c7Skishore kadiyala 	if (ret < 0) {
25872f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
25972f2e2c7Skishore kadiyala 									ret);
26072f2e2c7Skishore kadiyala 		return ret;
26172f2e2c7Skishore kadiyala 	}
26272f2e2c7Skishore kadiyala 	reg_val &= ~(MMC_PU | MMC_PD);
26372f2e2c7Skishore kadiyala 	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
26472f2e2c7Skishore kadiyala 						TWL6030_CFG_INPUT_PUPD3);
26572f2e2c7Skishore kadiyala 	if (ret < 0) {
26672f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
26772f2e2c7Skishore kadiyala 									ret);
26872f2e2c7Skishore kadiyala 		return ret;
26972f2e2c7Skishore kadiyala 	}
27072f2e2c7Skishore kadiyala 	return 0;
27172f2e2c7Skishore kadiyala }
27272f2e2c7Skishore kadiyala EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
27372f2e2c7Skishore kadiyala 
27472f2e2c7Skishore kadiyala int twl6030_mmc_card_detect(struct device *dev, int slot)
27572f2e2c7Skishore kadiyala {
27672f2e2c7Skishore kadiyala 	int ret = -EIO;
27772f2e2c7Skishore kadiyala 	u8 read_reg = 0;
27872f2e2c7Skishore kadiyala 	struct platform_device *pdev = to_platform_device(dev);
27972f2e2c7Skishore kadiyala 
28072f2e2c7Skishore kadiyala 	if (pdev->id) {
28172f2e2c7Skishore kadiyala 		/* TWL6030 provide's Card detect support for
28272f2e2c7Skishore kadiyala 		 * only MMC1 controller.
28372f2e2c7Skishore kadiyala 		 */
28472f2e2c7Skishore kadiyala 		pr_err("Unkown MMC controller %d in %s\n", pdev->id, __func__);
28572f2e2c7Skishore kadiyala 		return ret;
28672f2e2c7Skishore kadiyala 	}
28772f2e2c7Skishore kadiyala 	/*
28872f2e2c7Skishore kadiyala 	 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
28972f2e2c7Skishore kadiyala 	 * 0 - Card not present ,1 - Card present
29072f2e2c7Skishore kadiyala 	 */
29172f2e2c7Skishore kadiyala 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
29272f2e2c7Skishore kadiyala 						TWL6030_MMCCTRL);
29372f2e2c7Skishore kadiyala 	if (ret >= 0)
29472f2e2c7Skishore kadiyala 		ret = read_reg & STS_MMC;
29572f2e2c7Skishore kadiyala 	return ret;
29672f2e2c7Skishore kadiyala }
29772f2e2c7Skishore kadiyala EXPORT_SYMBOL(twl6030_mmc_card_detect);
29872f2e2c7Skishore kadiyala 
299e8deb28cSBalaji T K int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
300e8deb28cSBalaji T K {
301e8deb28cSBalaji T K 
302e8deb28cSBalaji T K 	int	status = 0;
303e8deb28cSBalaji T K 	int	i;
304e8deb28cSBalaji T K 	struct task_struct	*task;
305e8deb28cSBalaji T K 	int ret;
306e8deb28cSBalaji T K 	u8 mask[4];
307e8deb28cSBalaji T K 
308e8deb28cSBalaji T K 	static struct irq_chip	twl6030_irq_chip;
309e8deb28cSBalaji T K 	mask[1] = 0xFF;
310e8deb28cSBalaji T K 	mask[2] = 0xFF;
311e8deb28cSBalaji T K 	mask[3] = 0xFF;
312e8deb28cSBalaji T K 	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
313e8deb28cSBalaji T K 			REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */
314e8deb28cSBalaji T K 	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
315e8deb28cSBalaji T K 			REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */
316e8deb28cSBalaji T K 	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
317e8deb28cSBalaji T K 			REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
318e8deb28cSBalaji T K 
319e8deb28cSBalaji T K 	twl6030_irq_base = irq_base;
320e8deb28cSBalaji T K 
321e8deb28cSBalaji T K 	/* install an irq handler for each of the modules;
322e8deb28cSBalaji T K 	 * clone dummy irq_chip since PIH can't *do* anything
323e8deb28cSBalaji T K 	 */
324e8deb28cSBalaji T K 	twl6030_irq_chip = dummy_irq_chip;
325e8deb28cSBalaji T K 	twl6030_irq_chip.name = "twl6030";
326e8deb28cSBalaji T K 	twl6030_irq_chip.set_type = NULL;
327e8deb28cSBalaji T K 
328e8deb28cSBalaji T K 	for (i = irq_base; i < irq_end; i++) {
329e8deb28cSBalaji T K 		set_irq_chip_and_handler(i, &twl6030_irq_chip,
330e8deb28cSBalaji T K 				handle_simple_irq);
331e8deb28cSBalaji T K 		activate_irq(i);
332e8deb28cSBalaji T K 	}
333e8deb28cSBalaji T K 
334e8deb28cSBalaji T K 	twl6030_irq_next = i;
335e8deb28cSBalaji T K 	pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
336e8deb28cSBalaji T K 			irq_num, irq_base, twl6030_irq_next - 1);
337e8deb28cSBalaji T K 
338e8deb28cSBalaji T K 	/* install an irq handler to demultiplex the TWL6030 interrupt */
339e8deb28cSBalaji T K 	init_completion(&irq_event);
340e8deb28cSBalaji T K 	task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
341e8deb28cSBalaji T K 	if (IS_ERR(task)) {
342e8deb28cSBalaji T K 		pr_err("twl6030: could not create irq %d thread!\n", irq_num);
343e8deb28cSBalaji T K 		status = PTR_ERR(task);
344e8deb28cSBalaji T K 		goto fail_kthread;
345e8deb28cSBalaji T K 	}
346e8deb28cSBalaji T K 
347e8deb28cSBalaji T K 	status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
348e8deb28cSBalaji T K 				"TWL6030-PIH", &irq_event);
349e8deb28cSBalaji T K 	if (status < 0) {
350e8deb28cSBalaji T K 		pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
351e8deb28cSBalaji T K 		goto fail_irq;
352e8deb28cSBalaji T K 	}
353e8deb28cSBalaji T K 	return status;
354e8deb28cSBalaji T K fail_irq:
355e8deb28cSBalaji T K 	free_irq(irq_num, &irq_event);
356e8deb28cSBalaji T K 
357e8deb28cSBalaji T K fail_kthread:
358e8deb28cSBalaji T K 	for (i = irq_base; i < irq_end; i++)
359e8deb28cSBalaji T K 		set_irq_chip_and_handler(i, NULL, NULL);
360e8deb28cSBalaji T K 	return status;
361e8deb28cSBalaji T K }
362e8deb28cSBalaji T K 
363e8deb28cSBalaji T K int twl6030_exit_irq(void)
364e8deb28cSBalaji T K {
365e8deb28cSBalaji T K 
366e8deb28cSBalaji T K 	if (twl6030_irq_base) {
367e8deb28cSBalaji T K 		pr_err("twl6030: can't yet clean up IRQs?\n");
368e8deb28cSBalaji T K 		return -ENOSYS;
369e8deb28cSBalaji T K 	}
370e8deb28cSBalaji T K 	return 0;
371e8deb28cSBalaji T K }
372e8deb28cSBalaji T K 
373