xref: /openbmc/linux/drivers/mfd/twl6030-irq.c (revision 862de70c)
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 
41b0b4a7c2SG, Manjunath Kondaiah #include "twl-core.h"
42b0b4a7c2SG, Manjunath Kondaiah 
43e8deb28cSBalaji T K /*
44e8deb28cSBalaji T K  * TWL6030 (unlike its predecessors, which had two level interrupt handling)
45e8deb28cSBalaji T K  * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
46e8deb28cSBalaji T K  * It exposes status bits saying who has raised an interrupt. There are
47e8deb28cSBalaji T K  * three mask registers that corresponds to these status registers, that
48e8deb28cSBalaji T K  * enables/disables these interrupts.
49e8deb28cSBalaji T K  *
50e8deb28cSBalaji T K  * We set up IRQs starting at a platform-specified base. An interrupt map table,
51e8deb28cSBalaji T K  * specifies mapping between interrupt number and the associated module.
52e8deb28cSBalaji T K  *
53e8deb28cSBalaji T K  */
54e8deb28cSBalaji T K 
55e8deb28cSBalaji T K static int twl6030_interrupt_mapping[24] = {
56e8deb28cSBalaji T K 	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/
57e8deb28cSBalaji T K 	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/
58e8deb28cSBalaji T K 	PWR_INTR_OFFSET,	/* Bit 2	BAT_VLOW		*/
59e8deb28cSBalaji T K 	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/
60e8deb28cSBalaji T K 	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/
61e8deb28cSBalaji T K 	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/
62e8deb28cSBalaji T K 	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/
63e8deb28cSBalaji T K 	SMPSLDO_INTR_OFFSET,	/* Bit 7	VMMC_SHORT		*/
64e8deb28cSBalaji T K 
65e8deb28cSBalaji T K 	SMPSLDO_INTR_OFFSET,	/* Bit 8	VUSIM_SHORT		*/
66e8deb28cSBalaji T K 	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/
67e8deb28cSBalaji T K 	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/
68e8deb28cSBalaji T K 	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/
69e8deb28cSBalaji T K 	RSV_INTR_OFFSET,  	/* Bit 12	Reserved		*/
70e8deb28cSBalaji T K 	MADC_INTR_OFFSET,	/* Bit 13	GPADC_RT_EOC		*/
71e8deb28cSBalaji T K 	MADC_INTR_OFFSET,	/* Bit 14	GPADC_SW_EOC		*/
72e8deb28cSBalaji T K 	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/
73e8deb28cSBalaji T K 
74e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/
75e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/
76e8deb28cSBalaji T K 	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/
7777b1d3faSHema HK 	USB_PRES_INTR_OFFSET,	/* Bit 19	VBUS			*/
78e8deb28cSBalaji T K 	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/
796523b148SGraeme Gregory 	CHARGERFAULT_INTR_OFFSET,	/* Bit 21	EXT_CHRG	*/
806523b148SGraeme Gregory 	CHARGERFAULT_INTR_OFFSET,	/* Bit 22	INT_CHRG	*/
81e8deb28cSBalaji T K 	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/
82e8deb28cSBalaji T K };
83e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
84e8deb28cSBalaji T K 
85e8deb28cSBalaji T K static unsigned twl6030_irq_base;
86e8deb28cSBalaji T K 
87e8deb28cSBalaji T K static struct completion irq_event;
88e8deb28cSBalaji T K 
89e8deb28cSBalaji T K /*
90e8deb28cSBalaji T K  * This thread processes interrupts reported by the Primary Interrupt Handler.
91e8deb28cSBalaji T K  */
92e8deb28cSBalaji T K static int twl6030_irq_thread(void *data)
93e8deb28cSBalaji T K {
94e8deb28cSBalaji T K 	long irq = (long)data;
95e8deb28cSBalaji T K 	static unsigned i2c_errors;
96e8deb28cSBalaji T K 	static const unsigned max_i2c_errors = 100;
97e8deb28cSBalaji T K 	int ret;
98e8deb28cSBalaji T K 
99e8deb28cSBalaji T K 	current->flags |= PF_NOFREEZE;
100e8deb28cSBalaji T K 
101e8deb28cSBalaji T K 	while (!kthread_should_stop()) {
102e8deb28cSBalaji T K 		int i;
103e8deb28cSBalaji T K 		union {
104e8deb28cSBalaji T K 		u8 bytes[4];
105e8deb28cSBalaji T K 		u32 int_sts;
106e8deb28cSBalaji T K 		} sts;
107e8deb28cSBalaji T K 
108e8deb28cSBalaji T K 		/* Wait for IRQ, then read PIH irq status (also blocking) */
109e8deb28cSBalaji T K 		wait_for_completion_interruptible(&irq_event);
110e8deb28cSBalaji T K 
111e8deb28cSBalaji T K 		/* read INT_STS_A, B and C in one shot using a burst read */
112e8deb28cSBalaji T K 		ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
113e8deb28cSBalaji T K 				REG_INT_STS_A, 3);
114e8deb28cSBalaji T K 		if (ret) {
115e8deb28cSBalaji T K 			pr_warning("twl6030: I2C error %d reading PIH ISR\n",
116e8deb28cSBalaji T K 					ret);
117e8deb28cSBalaji T K 			if (++i2c_errors >= max_i2c_errors) {
118e8deb28cSBalaji T K 				printk(KERN_ERR "Maximum I2C error count"
119e8deb28cSBalaji T K 						" exceeded.  Terminating %s.\n",
120e8deb28cSBalaji T K 						__func__);
121e8deb28cSBalaji T K 				break;
122e8deb28cSBalaji T K 			}
123e8deb28cSBalaji T K 			complete(&irq_event);
124e8deb28cSBalaji T K 			continue;
125e8deb28cSBalaji T K 		}
126e8deb28cSBalaji T K 
127e8deb28cSBalaji T K 
128e8deb28cSBalaji T K 
129e8deb28cSBalaji T K 		sts.bytes[3] = 0; /* Only 24 bits are valid*/
130e8deb28cSBalaji T K 
13177b1d3faSHema HK 		/*
13277b1d3faSHema HK 		 * Since VBUS status bit is not reliable for VBUS disconnect
13377b1d3faSHema HK 		 * use CHARGER VBUS detection status bit instead.
13477b1d3faSHema HK 		 */
13577b1d3faSHema HK 		if (sts.bytes[2] & 0x10)
13677b1d3faSHema HK 			sts.bytes[2] |= 0x08;
13777b1d3faSHema HK 
138e8deb28cSBalaji T K 		for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
139e8deb28cSBalaji T K 			local_irq_disable();
140e8deb28cSBalaji T K 			if (sts.int_sts & 0x1) {
141e8deb28cSBalaji T K 				int module_irq = twl6030_irq_base +
142e8deb28cSBalaji T K 					twl6030_interrupt_mapping[i];
143c22435a3SThomas Gleixner 				generic_handle_irq(module_irq);
144e8deb28cSBalaji T K 
145e8deb28cSBalaji T K 			}
146e8deb28cSBalaji T K 		local_irq_enable();
147e8deb28cSBalaji T K 		}
148e8deb28cSBalaji T K 		ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
149e8deb28cSBalaji T K 				REG_INT_STS_A, 3); /* clear INT_STS_A */
150e8deb28cSBalaji T K 		if (ret)
151e8deb28cSBalaji T K 			pr_warning("twl6030: I2C error in clearing PIH ISR\n");
152e8deb28cSBalaji T K 
153e8deb28cSBalaji T K 		enable_irq(irq);
154e8deb28cSBalaji T K 	}
155e8deb28cSBalaji T K 
156e8deb28cSBalaji T K 	return 0;
157e8deb28cSBalaji T K }
158e8deb28cSBalaji T K 
159e8deb28cSBalaji T K /*
160e8deb28cSBalaji T K  * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
161e8deb28cSBalaji T K  * This is a chained interrupt, so there is no desc->action method for it.
162e8deb28cSBalaji T K  * Now we need to query the interrupt controller in the twl6030 to determine
163e8deb28cSBalaji T K  * which module is generating the interrupt request.  However, we can't do i2c
164e8deb28cSBalaji T K  * transactions in interrupt context, so we must defer that work to a kernel
165e8deb28cSBalaji T K  * thread.  All we do here is acknowledge and mask the interrupt and wakeup
166e8deb28cSBalaji T K  * the kernel thread.
167e8deb28cSBalaji T K  */
168e8deb28cSBalaji T K static irqreturn_t handle_twl6030_pih(int irq, void *devid)
169e8deb28cSBalaji T K {
170e8deb28cSBalaji T K 	disable_irq_nosync(irq);
171e8deb28cSBalaji T K 	complete(devid);
172e8deb28cSBalaji T K 	return IRQ_HANDLED;
173e8deb28cSBalaji T K }
174e8deb28cSBalaji T K 
175e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
176e8deb28cSBalaji T K 
177e8deb28cSBalaji T K static inline void activate_irq(int irq)
178e8deb28cSBalaji T K {
179e8deb28cSBalaji T K #ifdef CONFIG_ARM
180e8deb28cSBalaji T K 	/* ARM requires an extra step to clear IRQ_NOREQUEST, which it
181e8deb28cSBalaji T K 	 * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
182e8deb28cSBalaji T K 	 */
183e8deb28cSBalaji T K 	set_irq_flags(irq, IRQF_VALID);
184e8deb28cSBalaji T K #else
185e8deb28cSBalaji T K 	/* same effect on other architectures */
186d5bb1221SThomas Gleixner 	irq_set_noprobe(irq);
187e8deb28cSBalaji T K #endif
188e8deb28cSBalaji T K }
189e8deb28cSBalaji T K 
190e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
191e8deb28cSBalaji T K 
192e8deb28cSBalaji T K static unsigned twl6030_irq_next;
193e8deb28cSBalaji T K 
194e8deb28cSBalaji T K /*----------------------------------------------------------------------*/
195e8deb28cSBalaji T K int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
196e8deb28cSBalaji T K {
197e8deb28cSBalaji T K 	int ret;
198e8deb28cSBalaji T K 	u8 unmask_value;
199e8deb28cSBalaji T K 	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
200e8deb28cSBalaji T K 			REG_INT_STS_A + offset);
201e8deb28cSBalaji T K 	unmask_value &= (~(bit_mask));
202e8deb28cSBalaji T K 	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
203e8deb28cSBalaji T K 			REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
204e8deb28cSBalaji T K 	return ret;
205e8deb28cSBalaji T K }
206e8deb28cSBalaji T K EXPORT_SYMBOL(twl6030_interrupt_unmask);
207e8deb28cSBalaji T K 
208e8deb28cSBalaji T K int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
209e8deb28cSBalaji T K {
210e8deb28cSBalaji T K 	int ret;
211e8deb28cSBalaji T K 	u8 mask_value;
212e8deb28cSBalaji T K 	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
213e8deb28cSBalaji T K 			REG_INT_STS_A + offset);
214e8deb28cSBalaji T K 	mask_value |= (bit_mask);
215e8deb28cSBalaji T K 	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
216e8deb28cSBalaji T K 			REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
217e8deb28cSBalaji T K 	return ret;
218e8deb28cSBalaji T K }
219e8deb28cSBalaji T K EXPORT_SYMBOL(twl6030_interrupt_mask);
220e8deb28cSBalaji T K 
22172f2e2c7Skishore kadiyala int twl6030_mmc_card_detect_config(void)
22272f2e2c7Skishore kadiyala {
22372f2e2c7Skishore kadiyala 	int ret;
22472f2e2c7Skishore kadiyala 	u8 reg_val = 0;
22572f2e2c7Skishore kadiyala 
22672f2e2c7Skishore kadiyala 	/* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
22772f2e2c7Skishore kadiyala 	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
22872f2e2c7Skishore kadiyala 						REG_INT_MSK_LINE_B);
22972f2e2c7Skishore kadiyala 	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
23072f2e2c7Skishore kadiyala 						REG_INT_MSK_STS_B);
23172f2e2c7Skishore kadiyala 	/*
23225985edcSLucas De Marchi 	 * Initially Configuring MMC_CTRL for receiving interrupts &
23372f2e2c7Skishore kadiyala 	 * Card status on TWL6030 for MMC1
23472f2e2c7Skishore kadiyala 	 */
23572f2e2c7Skishore kadiyala 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
23672f2e2c7Skishore kadiyala 	if (ret < 0) {
23772f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
23872f2e2c7Skishore kadiyala 		return ret;
23972f2e2c7Skishore kadiyala 	}
24072f2e2c7Skishore kadiyala 	reg_val &= ~VMMC_AUTO_OFF;
24172f2e2c7Skishore kadiyala 	reg_val |= SW_FC;
24272f2e2c7Skishore kadiyala 	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
24372f2e2c7Skishore kadiyala 	if (ret < 0) {
24472f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
24572f2e2c7Skishore kadiyala 		return ret;
24672f2e2c7Skishore kadiyala 	}
24772f2e2c7Skishore kadiyala 
24872f2e2c7Skishore kadiyala 	/* Configuring PullUp-PullDown register */
24972f2e2c7Skishore kadiyala 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
25072f2e2c7Skishore kadiyala 						TWL6030_CFG_INPUT_PUPD3);
25172f2e2c7Skishore kadiyala 	if (ret < 0) {
25272f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
25372f2e2c7Skishore kadiyala 									ret);
25472f2e2c7Skishore kadiyala 		return ret;
25572f2e2c7Skishore kadiyala 	}
25672f2e2c7Skishore kadiyala 	reg_val &= ~(MMC_PU | MMC_PD);
25772f2e2c7Skishore kadiyala 	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
25872f2e2c7Skishore kadiyala 						TWL6030_CFG_INPUT_PUPD3);
25972f2e2c7Skishore kadiyala 	if (ret < 0) {
26072f2e2c7Skishore kadiyala 		pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
26172f2e2c7Skishore kadiyala 									ret);
26272f2e2c7Skishore kadiyala 		return ret;
26372f2e2c7Skishore kadiyala 	}
26472f2e2c7Skishore kadiyala 	return 0;
26572f2e2c7Skishore kadiyala }
26672f2e2c7Skishore kadiyala EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
26772f2e2c7Skishore kadiyala 
26872f2e2c7Skishore kadiyala int twl6030_mmc_card_detect(struct device *dev, int slot)
26972f2e2c7Skishore kadiyala {
27072f2e2c7Skishore kadiyala 	int ret = -EIO;
27172f2e2c7Skishore kadiyala 	u8 read_reg = 0;
27272f2e2c7Skishore kadiyala 	struct platform_device *pdev = to_platform_device(dev);
27372f2e2c7Skishore kadiyala 
27472f2e2c7Skishore kadiyala 	if (pdev->id) {
27572f2e2c7Skishore kadiyala 		/* TWL6030 provide's Card detect support for
27672f2e2c7Skishore kadiyala 		 * only MMC1 controller.
27772f2e2c7Skishore kadiyala 		 */
27825985edcSLucas De Marchi 		pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__);
27972f2e2c7Skishore kadiyala 		return ret;
28072f2e2c7Skishore kadiyala 	}
28172f2e2c7Skishore kadiyala 	/*
28272f2e2c7Skishore kadiyala 	 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
28372f2e2c7Skishore kadiyala 	 * 0 - Card not present ,1 - Card present
28472f2e2c7Skishore kadiyala 	 */
28572f2e2c7Skishore kadiyala 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
28672f2e2c7Skishore kadiyala 						TWL6030_MMCCTRL);
28772f2e2c7Skishore kadiyala 	if (ret >= 0)
28872f2e2c7Skishore kadiyala 		ret = read_reg & STS_MMC;
28972f2e2c7Skishore kadiyala 	return ret;
29072f2e2c7Skishore kadiyala }
29172f2e2c7Skishore kadiyala EXPORT_SYMBOL(twl6030_mmc_card_detect);
29272f2e2c7Skishore kadiyala 
293e8deb28cSBalaji T K int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
294e8deb28cSBalaji T K {
295e8deb28cSBalaji T K 
296e8deb28cSBalaji T K 	int	status = 0;
297e8deb28cSBalaji T K 	int	i;
298e8deb28cSBalaji T K 	struct task_struct	*task;
299e8deb28cSBalaji T K 	int ret;
300e8deb28cSBalaji T K 	u8 mask[4];
301e8deb28cSBalaji T K 
302e8deb28cSBalaji T K 	static struct irq_chip	twl6030_irq_chip;
303e8deb28cSBalaji T K 	mask[1] = 0xFF;
304e8deb28cSBalaji T K 	mask[2] = 0xFF;
305e8deb28cSBalaji T K 	mask[3] = 0xFF;
306e8deb28cSBalaji T K 	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
307e8deb28cSBalaji T K 			REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */
308e8deb28cSBalaji T K 	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
309e8deb28cSBalaji T K 			REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */
310e8deb28cSBalaji T K 	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
311e8deb28cSBalaji T K 			REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
312e8deb28cSBalaji T K 
313e8deb28cSBalaji T K 	twl6030_irq_base = irq_base;
314e8deb28cSBalaji T K 
315e8deb28cSBalaji T K 	/* install an irq handler for each of the modules;
316e8deb28cSBalaji T K 	 * clone dummy irq_chip since PIH can't *do* anything
317e8deb28cSBalaji T K 	 */
318e8deb28cSBalaji T K 	twl6030_irq_chip = dummy_irq_chip;
319e8deb28cSBalaji T K 	twl6030_irq_chip.name = "twl6030";
320c45c685cSLennert Buytenhek 	twl6030_irq_chip.irq_set_type = NULL;
321e8deb28cSBalaji T K 
322e8deb28cSBalaji T K 	for (i = irq_base; i < irq_end; i++) {
323d5bb1221SThomas Gleixner 		irq_set_chip_and_handler(i, &twl6030_irq_chip,
324e8deb28cSBalaji T K 					 handle_simple_irq);
325e8deb28cSBalaji T K 		activate_irq(i);
326e8deb28cSBalaji T K 	}
327e8deb28cSBalaji T K 
328e8deb28cSBalaji T K 	twl6030_irq_next = i;
329e8deb28cSBalaji T K 	pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
330e8deb28cSBalaji T K 			irq_num, irq_base, twl6030_irq_next - 1);
331e8deb28cSBalaji T K 
332e8deb28cSBalaji T K 	/* install an irq handler to demultiplex the TWL6030 interrupt */
333e8deb28cSBalaji T K 	init_completion(&irq_event);
334e8deb28cSBalaji T K 
335e8deb28cSBalaji T K 	status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
336e8deb28cSBalaji T K 				"TWL6030-PIH", &irq_event);
337e8deb28cSBalaji T K 	if (status < 0) {
338e8deb28cSBalaji T K 		pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
339e8deb28cSBalaji T K 		goto fail_irq;
340e8deb28cSBalaji T K 	}
341862de70cSAxel Lin 
342862de70cSAxel Lin 	task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
343862de70cSAxel Lin 	if (IS_ERR(task)) {
344862de70cSAxel Lin 		pr_err("twl6030: could not create irq %d thread!\n", irq_num);
345862de70cSAxel Lin 		status = PTR_ERR(task);
346862de70cSAxel Lin 		goto fail_kthread;
347862de70cSAxel Lin 	}
348e8deb28cSBalaji T K 	return status;
349e8deb28cSBalaji T K 
350e8deb28cSBalaji T K fail_kthread:
351862de70cSAxel Lin 	free_irq(irq_num, &irq_event);
352862de70cSAxel Lin 
353862de70cSAxel Lin fail_irq:
354e8deb28cSBalaji T K 	for (i = irq_base; i < irq_end; i++)
355d5bb1221SThomas Gleixner 		irq_set_chip_and_handler(i, NULL, NULL);
356e8deb28cSBalaji T K 	return status;
357e8deb28cSBalaji T K }
358e8deb28cSBalaji T K 
359e8deb28cSBalaji T K int twl6030_exit_irq(void)
360e8deb28cSBalaji T K {
361e8deb28cSBalaji T K 
362e8deb28cSBalaji T K 	if (twl6030_irq_base) {
363e8deb28cSBalaji T K 		pr_err("twl6030: can't yet clean up IRQs?\n");
364e8deb28cSBalaji T K 		return -ENOSYS;
365e8deb28cSBalaji T K 	}
366e8deb28cSBalaji T K 	return 0;
367e8deb28cSBalaji T K }
368e8deb28cSBalaji T K 
369