input: Touchscreen driver for TPS6507x
[linux-2.6.git] / drivers / mfd / twl6030-irq.c
1 /*
2  * twl6030-irq.c - TWL6030 irq support
3  *
4  * Copyright (C) 2005-2009 Texas Instruments, Inc.
5  *
6  * Modifications to defer interrupt handling to a kernel thread:
7  * Copyright (C) 2006 MontaVista Software, Inc.
8  *
9  * Based on tlv320aic23.c:
10  * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
11  *
12  * Code cleanup and modifications to IRQ handler.
13  * by syed khasim <x0khasim@ti.com>
14  *
15  * TWL6030 specific code and IRQ handling changes by
16  * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
17  * Balaji T K <balajitk@ti.com>
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
32  */
33
34 #include <linux/init.h>
35 #include <linux/interrupt.h>
36 #include <linux/irq.h>
37 #include <linux/kthread.h>
38 #include <linux/i2c/twl.h>
39
40 /*
41  * TWL6030 (unlike its predecessors, which had two level interrupt handling)
42  * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
43  * It exposes status bits saying who has raised an interrupt. There are
44  * three mask registers that corresponds to these status registers, that
45  * enables/disables these interrupts.
46  *
47  * We set up IRQs starting at a platform-specified base. An interrupt map table,
48  * specifies mapping between interrupt number and the associated module.
49  *
50  */
51
52 static int twl6030_interrupt_mapping[24] = {
53         PWR_INTR_OFFSET,        /* Bit 0        PWRON                   */
54         PWR_INTR_OFFSET,        /* Bit 1        RPWRON                  */
55         PWR_INTR_OFFSET,        /* Bit 2        BAT_VLOW                */
56         RTC_INTR_OFFSET,        /* Bit 3        RTC_ALARM               */
57         RTC_INTR_OFFSET,        /* Bit 4        RTC_PERIOD              */
58         HOTDIE_INTR_OFFSET,     /* Bit 5        HOT_DIE                 */
59         SMPSLDO_INTR_OFFSET,    /* Bit 6        VXXX_SHORT              */
60         SMPSLDO_INTR_OFFSET,    /* Bit 7        VMMC_SHORT              */
61
62         SMPSLDO_INTR_OFFSET,    /* Bit 8        VUSIM_SHORT             */
63         BATDETECT_INTR_OFFSET,  /* Bit 9        BAT                     */
64         SIMDETECT_INTR_OFFSET,  /* Bit 10       SIM                     */
65         MMCDETECT_INTR_OFFSET,  /* Bit 11       MMC                     */
66         RSV_INTR_OFFSET,        /* Bit 12       Reserved                */
67         MADC_INTR_OFFSET,       /* Bit 13       GPADC_RT_EOC            */
68         MADC_INTR_OFFSET,       /* Bit 14       GPADC_SW_EOC            */
69         GASGAUGE_INTR_OFFSET,   /* Bit 15       CC_AUTOCAL              */
70
71         USBOTG_INTR_OFFSET,     /* Bit 16       ID_WKUP                 */
72         USBOTG_INTR_OFFSET,     /* Bit 17       VBUS_WKUP               */
73         USBOTG_INTR_OFFSET,     /* Bit 18       ID                      */
74         USBOTG_INTR_OFFSET,     /* Bit 19       VBUS                    */
75         CHARGER_INTR_OFFSET,    /* Bit 20       CHRG_CTRL               */
76         CHARGER_INTR_OFFSET,    /* Bit 21       EXT_CHRG                */
77         CHARGER_INTR_OFFSET,    /* Bit 22       INT_CHRG                */
78         RSV_INTR_OFFSET,        /* Bit 23       Reserved                */
79 };
80 /*----------------------------------------------------------------------*/
81
82 static unsigned twl6030_irq_base;
83
84 static struct completion irq_event;
85
86 /*
87  * This thread processes interrupts reported by the Primary Interrupt Handler.
88  */
89 static int twl6030_irq_thread(void *data)
90 {
91         long irq = (long)data;
92         static unsigned i2c_errors;
93         static const unsigned max_i2c_errors = 100;
94         int ret;
95
96         current->flags |= PF_NOFREEZE;
97
98         while (!kthread_should_stop()) {
99                 int i;
100                 union {
101                 u8 bytes[4];
102                 u32 int_sts;
103                 } sts;
104
105                 /* Wait for IRQ, then read PIH irq status (also blocking) */
106                 wait_for_completion_interruptible(&irq_event);
107
108                 /* read INT_STS_A, B and C in one shot using a burst read */
109                 ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
110                                 REG_INT_STS_A, 3);
111                 if (ret) {
112                         pr_warning("twl6030: I2C error %d reading PIH ISR\n",
113                                         ret);
114                         if (++i2c_errors >= max_i2c_errors) {
115                                 printk(KERN_ERR "Maximum I2C error count"
116                                                 " exceeded.  Terminating %s.\n",
117                                                 __func__);
118                                 break;
119                         }
120                         complete(&irq_event);
121                         continue;
122                 }
123
124
125
126                 sts.bytes[3] = 0; /* Only 24 bits are valid*/
127
128                 for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
129                         local_irq_disable();
130                         if (sts.int_sts & 0x1) {
131                                 int module_irq = twl6030_irq_base +
132                                         twl6030_interrupt_mapping[i];
133                                 struct irq_desc *d = irq_to_desc(module_irq);
134
135                                 if (!d) {
136                                         pr_err("twl6030: Invalid SIH IRQ: %d\n",
137                                                module_irq);
138                                         return -EINVAL;
139                                 }
140
141                                 /* These can't be masked ... always warn
142                                  * if we get any surprises.
143                                  */
144                                 if (d->status & IRQ_DISABLED)
145                                         note_interrupt(module_irq, d,
146                                                         IRQ_NONE);
147                                 else
148                                         d->handle_irq(module_irq, d);
149
150                         }
151                 local_irq_enable();
152                 }
153                 ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
154                                 REG_INT_STS_A, 3); /* clear INT_STS_A */
155                 if (ret)
156                         pr_warning("twl6030: I2C error in clearing PIH ISR\n");
157
158                 enable_irq(irq);
159         }
160
161         return 0;
162 }
163
164 /*
165  * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
166  * This is a chained interrupt, so there is no desc->action method for it.
167  * Now we need to query the interrupt controller in the twl6030 to determine
168  * which module is generating the interrupt request.  However, we can't do i2c
169  * transactions in interrupt context, so we must defer that work to a kernel
170  * thread.  All we do here is acknowledge and mask the interrupt and wakeup
171  * the kernel thread.
172  */
173 static irqreturn_t handle_twl6030_pih(int irq, void *devid)
174 {
175         disable_irq_nosync(irq);
176         complete(devid);
177         return IRQ_HANDLED;
178 }
179
180 /*----------------------------------------------------------------------*/
181
182 static inline void activate_irq(int irq)
183 {
184 #ifdef CONFIG_ARM
185         /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
186          * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
187          */
188         set_irq_flags(irq, IRQF_VALID);
189 #else
190         /* same effect on other architectures */
191         set_irq_noprobe(irq);
192 #endif
193 }
194
195 /*----------------------------------------------------------------------*/
196
197 static unsigned twl6030_irq_next;
198
199 /*----------------------------------------------------------------------*/
200 int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
201 {
202         int ret;
203         u8 unmask_value;
204         ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
205                         REG_INT_STS_A + offset);
206         unmask_value &= (~(bit_mask));
207         ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
208                         REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
209         return ret;
210 }
211 EXPORT_SYMBOL(twl6030_interrupt_unmask);
212
213 int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
214 {
215         int ret;
216         u8 mask_value;
217         ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
218                         REG_INT_STS_A + offset);
219         mask_value |= (bit_mask);
220         ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
221                         REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
222         return ret;
223 }
224 EXPORT_SYMBOL(twl6030_interrupt_mask);
225
226 int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
227 {
228
229         int     status = 0;
230         int     i;
231         struct task_struct      *task;
232         int ret;
233         u8 mask[4];
234
235         static struct irq_chip  twl6030_irq_chip;
236         mask[1] = 0xFF;
237         mask[2] = 0xFF;
238         mask[3] = 0xFF;
239         ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
240                         REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */
241         ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
242                         REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */
243         ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
244                         REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
245
246         twl6030_irq_base = irq_base;
247
248         /* install an irq handler for each of the modules;
249          * clone dummy irq_chip since PIH can't *do* anything
250          */
251         twl6030_irq_chip = dummy_irq_chip;
252         twl6030_irq_chip.name = "twl6030";
253         twl6030_irq_chip.set_type = NULL;
254
255         for (i = irq_base; i < irq_end; i++) {
256                 set_irq_chip_and_handler(i, &twl6030_irq_chip,
257                                 handle_simple_irq);
258                 activate_irq(i);
259         }
260
261         twl6030_irq_next = i;
262         pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
263                         irq_num, irq_base, twl6030_irq_next - 1);
264
265         /* install an irq handler to demultiplex the TWL6030 interrupt */
266         init_completion(&irq_event);
267         task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
268         if (IS_ERR(task)) {
269                 pr_err("twl6030: could not create irq %d thread!\n", irq_num);
270                 status = PTR_ERR(task);
271                 goto fail_kthread;
272         }
273
274         status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
275                                 "TWL6030-PIH", &irq_event);
276         if (status < 0) {
277                 pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
278                 goto fail_irq;
279         }
280         return status;
281 fail_irq:
282         free_irq(irq_num, &irq_event);
283
284 fail_kthread:
285         for (i = irq_base; i < irq_end; i++)
286                 set_irq_chip_and_handler(i, NULL, NULL);
287         return status;
288 }
289
290 int twl6030_exit_irq(void)
291 {
292
293         if (twl6030_irq_base) {
294                 pr_err("twl6030: can't yet clean up IRQs?\n");
295                 return -ENOSYS;
296         }
297         return 0;
298 }
299