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 #include <linux/platform_device.h> 40 41 #include "twl-core.h" 42 43 /* 44 * TWL6030 (unlike its predecessors, which had two level interrupt handling) 45 * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C. 46 * It exposes status bits saying who has raised an interrupt. There are 47 * three mask registers that corresponds to these status registers, that 48 * enables/disables these interrupts. 49 * 50 * We set up IRQs starting at a platform-specified base. An interrupt map table, 51 * specifies mapping between interrupt number and the associated module. 52 * 53 */ 54 55 static int twl6030_interrupt_mapping[24] = { 56 PWR_INTR_OFFSET, /* Bit 0 PWRON */ 57 PWR_INTR_OFFSET, /* Bit 1 RPWRON */ 58 PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */ 59 RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */ 60 RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */ 61 HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */ 62 SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */ 63 SMPSLDO_INTR_OFFSET, /* Bit 7 VMMC_SHORT */ 64 65 SMPSLDO_INTR_OFFSET, /* Bit 8 VUSIM_SHORT */ 66 BATDETECT_INTR_OFFSET, /* Bit 9 BAT */ 67 SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */ 68 MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */ 69 RSV_INTR_OFFSET, /* Bit 12 Reserved */ 70 MADC_INTR_OFFSET, /* Bit 13 GPADC_RT_EOC */ 71 MADC_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */ 72 GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */ 73 74 USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */ 75 USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */ 76 USBOTG_INTR_OFFSET, /* Bit 18 ID */ 77 USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */ 78 CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */ 79 CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */ 80 CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */ 81 RSV_INTR_OFFSET, /* Bit 23 Reserved */ 82 }; 83 /*----------------------------------------------------------------------*/ 84 85 static unsigned twl6030_irq_base; 86 87 static struct completion irq_event; 88 89 /* 90 * This thread processes interrupts reported by the Primary Interrupt Handler. 91 */ 92 static int twl6030_irq_thread(void *data) 93 { 94 long irq = (long)data; 95 static unsigned i2c_errors; 96 static const unsigned max_i2c_errors = 100; 97 int ret; 98 99 current->flags |= PF_NOFREEZE; 100 101 while (!kthread_should_stop()) { 102 int i; 103 union { 104 u8 bytes[4]; 105 u32 int_sts; 106 } sts; 107 108 /* Wait for IRQ, then read PIH irq status (also blocking) */ 109 wait_for_completion_interruptible(&irq_event); 110 111 /* read INT_STS_A, B and C in one shot using a burst read */ 112 ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, 113 REG_INT_STS_A, 3); 114 if (ret) { 115 pr_warning("twl6030: I2C error %d reading PIH ISR\n", 116 ret); 117 if (++i2c_errors >= max_i2c_errors) { 118 printk(KERN_ERR "Maximum I2C error count" 119 " exceeded. Terminating %s.\n", 120 __func__); 121 break; 122 } 123 complete(&irq_event); 124 continue; 125 } 126 127 128 129 sts.bytes[3] = 0; /* Only 24 bits are valid*/ 130 131 /* 132 * Since VBUS status bit is not reliable for VBUS disconnect 133 * use CHARGER VBUS detection status bit instead. 134 */ 135 if (sts.bytes[2] & 0x10) 136 sts.bytes[2] |= 0x08; 137 138 for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) { 139 local_irq_disable(); 140 if (sts.int_sts & 0x1) { 141 int module_irq = twl6030_irq_base + 142 twl6030_interrupt_mapping[i]; 143 struct irq_desc *d = irq_to_desc(module_irq); 144 145 if (!d) { 146 pr_err("twl6030: Invalid SIH IRQ: %d\n", 147 module_irq); 148 return -EINVAL; 149 } 150 151 /* These can't be masked ... always warn 152 * if we get any surprises. 153 */ 154 if (d->status & IRQ_DISABLED) 155 note_interrupt(module_irq, d, 156 IRQ_NONE); 157 else 158 d->handle_irq(module_irq, d); 159 160 } 161 local_irq_enable(); 162 } 163 ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes, 164 REG_INT_STS_A, 3); /* clear INT_STS_A */ 165 if (ret) 166 pr_warning("twl6030: I2C error in clearing PIH ISR\n"); 167 168 enable_irq(irq); 169 } 170 171 return 0; 172 } 173 174 /* 175 * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt. 176 * This is a chained interrupt, so there is no desc->action method for it. 177 * Now we need to query the interrupt controller in the twl6030 to determine 178 * which module is generating the interrupt request. However, we can't do i2c 179 * transactions in interrupt context, so we must defer that work to a kernel 180 * thread. All we do here is acknowledge and mask the interrupt and wakeup 181 * the kernel thread. 182 */ 183 static irqreturn_t handle_twl6030_pih(int irq, void *devid) 184 { 185 disable_irq_nosync(irq); 186 complete(devid); 187 return IRQ_HANDLED; 188 } 189 190 /*----------------------------------------------------------------------*/ 191 192 static inline void activate_irq(int irq) 193 { 194 #ifdef CONFIG_ARM 195 /* ARM requires an extra step to clear IRQ_NOREQUEST, which it 196 * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. 197 */ 198 set_irq_flags(irq, IRQF_VALID); 199 #else 200 /* same effect on other architectures */ 201 set_irq_noprobe(irq); 202 #endif 203 } 204 205 /*----------------------------------------------------------------------*/ 206 207 static unsigned twl6030_irq_next; 208 209 /*----------------------------------------------------------------------*/ 210 int twl6030_interrupt_unmask(u8 bit_mask, u8 offset) 211 { 212 int ret; 213 u8 unmask_value; 214 ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value, 215 REG_INT_STS_A + offset); 216 unmask_value &= (~(bit_mask)); 217 ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value, 218 REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */ 219 return ret; 220 } 221 EXPORT_SYMBOL(twl6030_interrupt_unmask); 222 223 int twl6030_interrupt_mask(u8 bit_mask, u8 offset) 224 { 225 int ret; 226 u8 mask_value; 227 ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value, 228 REG_INT_STS_A + offset); 229 mask_value |= (bit_mask); 230 ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value, 231 REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */ 232 return ret; 233 } 234 EXPORT_SYMBOL(twl6030_interrupt_mask); 235 236 int twl6030_mmc_card_detect_config(void) 237 { 238 int ret; 239 u8 reg_val = 0; 240 241 /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */ 242 twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, 243 REG_INT_MSK_LINE_B); 244 twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, 245 REG_INT_MSK_STS_B); 246 /* 247 * Intially Configuring MMC_CTRL for receving interrupts & 248 * Card status on TWL6030 for MMC1 249 */ 250 ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, TWL6030_MMCCTRL); 251 if (ret < 0) { 252 pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret); 253 return ret; 254 } 255 reg_val &= ~VMMC_AUTO_OFF; 256 reg_val |= SW_FC; 257 ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL); 258 if (ret < 0) { 259 pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret); 260 return ret; 261 } 262 263 /* Configuring PullUp-PullDown register */ 264 ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, 265 TWL6030_CFG_INPUT_PUPD3); 266 if (ret < 0) { 267 pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n", 268 ret); 269 return ret; 270 } 271 reg_val &= ~(MMC_PU | MMC_PD); 272 ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, 273 TWL6030_CFG_INPUT_PUPD3); 274 if (ret < 0) { 275 pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n", 276 ret); 277 return ret; 278 } 279 return 0; 280 } 281 EXPORT_SYMBOL(twl6030_mmc_card_detect_config); 282 283 int twl6030_mmc_card_detect(struct device *dev, int slot) 284 { 285 int ret = -EIO; 286 u8 read_reg = 0; 287 struct platform_device *pdev = to_platform_device(dev); 288 289 if (pdev->id) { 290 /* TWL6030 provide's Card detect support for 291 * only MMC1 controller. 292 */ 293 pr_err("Unkown MMC controller %d in %s\n", pdev->id, __func__); 294 return ret; 295 } 296 /* 297 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1 298 * 0 - Card not present ,1 - Card present 299 */ 300 ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg, 301 TWL6030_MMCCTRL); 302 if (ret >= 0) 303 ret = read_reg & STS_MMC; 304 return ret; 305 } 306 EXPORT_SYMBOL(twl6030_mmc_card_detect); 307 308 int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) 309 { 310 311 int status = 0; 312 int i; 313 struct task_struct *task; 314 int ret; 315 u8 mask[4]; 316 317 static struct irq_chip twl6030_irq_chip; 318 mask[1] = 0xFF; 319 mask[2] = 0xFF; 320 mask[3] = 0xFF; 321 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], 322 REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */ 323 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], 324 REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */ 325 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], 326 REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */ 327 328 twl6030_irq_base = irq_base; 329 330 /* install an irq handler for each of the modules; 331 * clone dummy irq_chip since PIH can't *do* anything 332 */ 333 twl6030_irq_chip = dummy_irq_chip; 334 twl6030_irq_chip.name = "twl6030"; 335 twl6030_irq_chip.irq_set_type = NULL; 336 337 for (i = irq_base; i < irq_end; i++) { 338 set_irq_chip_and_handler(i, &twl6030_irq_chip, 339 handle_simple_irq); 340 activate_irq(i); 341 } 342 343 twl6030_irq_next = i; 344 pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", 345 irq_num, irq_base, twl6030_irq_next - 1); 346 347 /* install an irq handler to demultiplex the TWL6030 interrupt */ 348 init_completion(&irq_event); 349 task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); 350 if (IS_ERR(task)) { 351 pr_err("twl6030: could not create irq %d thread!\n", irq_num); 352 status = PTR_ERR(task); 353 goto fail_kthread; 354 } 355 356 status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED, 357 "TWL6030-PIH", &irq_event); 358 if (status < 0) { 359 pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); 360 goto fail_irq; 361 } 362 return status; 363 fail_irq: 364 free_irq(irq_num, &irq_event); 365 366 fail_kthread: 367 for (i = irq_base; i < irq_end; i++) 368 set_irq_chip_and_handler(i, NULL, NULL); 369 return status; 370 } 371 372 int twl6030_exit_irq(void) 373 { 374 375 if (twl6030_irq_base) { 376 pr_err("twl6030: can't yet clean up IRQs?\n"); 377 return -ENOSYS; 378 } 379 return 0; 380 } 381 382