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 generic_handle_irq(module_irq); 144 145 } 146 local_irq_enable(); 147 } 148 ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes, 149 REG_INT_STS_A, 3); /* clear INT_STS_A */ 150 if (ret) 151 pr_warning("twl6030: I2C error in clearing PIH ISR\n"); 152 153 enable_irq(irq); 154 } 155 156 return 0; 157 } 158 159 /* 160 * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt. 161 * This is a chained interrupt, so there is no desc->action method for it. 162 * Now we need to query the interrupt controller in the twl6030 to determine 163 * which module is generating the interrupt request. However, we can't do i2c 164 * transactions in interrupt context, so we must defer that work to a kernel 165 * thread. All we do here is acknowledge and mask the interrupt and wakeup 166 * the kernel thread. 167 */ 168 static irqreturn_t handle_twl6030_pih(int irq, void *devid) 169 { 170 disable_irq_nosync(irq); 171 complete(devid); 172 return IRQ_HANDLED; 173 } 174 175 /*----------------------------------------------------------------------*/ 176 177 static inline void activate_irq(int irq) 178 { 179 #ifdef CONFIG_ARM 180 /* ARM requires an extra step to clear IRQ_NOREQUEST, which it 181 * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. 182 */ 183 set_irq_flags(irq, IRQF_VALID); 184 #else 185 /* same effect on other architectures */ 186 irq_set_noprobe(irq); 187 #endif 188 } 189 190 /*----------------------------------------------------------------------*/ 191 192 static unsigned twl6030_irq_next; 193 194 /*----------------------------------------------------------------------*/ 195 int twl6030_interrupt_unmask(u8 bit_mask, u8 offset) 196 { 197 int ret; 198 u8 unmask_value; 199 ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value, 200 REG_INT_STS_A + offset); 201 unmask_value &= (~(bit_mask)); 202 ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value, 203 REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */ 204 return ret; 205 } 206 EXPORT_SYMBOL(twl6030_interrupt_unmask); 207 208 int twl6030_interrupt_mask(u8 bit_mask, u8 offset) 209 { 210 int ret; 211 u8 mask_value; 212 ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value, 213 REG_INT_STS_A + offset); 214 mask_value |= (bit_mask); 215 ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value, 216 REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */ 217 return ret; 218 } 219 EXPORT_SYMBOL(twl6030_interrupt_mask); 220 221 int twl6030_mmc_card_detect_config(void) 222 { 223 int ret; 224 u8 reg_val = 0; 225 226 /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */ 227 twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, 228 REG_INT_MSK_LINE_B); 229 twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, 230 REG_INT_MSK_STS_B); 231 /* 232 * Initially Configuring MMC_CTRL for receiving interrupts & 233 * Card status on TWL6030 for MMC1 234 */ 235 ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, TWL6030_MMCCTRL); 236 if (ret < 0) { 237 pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret); 238 return ret; 239 } 240 reg_val &= ~VMMC_AUTO_OFF; 241 reg_val |= SW_FC; 242 ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL); 243 if (ret < 0) { 244 pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret); 245 return ret; 246 } 247 248 /* Configuring PullUp-PullDown register */ 249 ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, 250 TWL6030_CFG_INPUT_PUPD3); 251 if (ret < 0) { 252 pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n", 253 ret); 254 return ret; 255 } 256 reg_val &= ~(MMC_PU | MMC_PD); 257 ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, 258 TWL6030_CFG_INPUT_PUPD3); 259 if (ret < 0) { 260 pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n", 261 ret); 262 return ret; 263 } 264 return 0; 265 } 266 EXPORT_SYMBOL(twl6030_mmc_card_detect_config); 267 268 int twl6030_mmc_card_detect(struct device *dev, int slot) 269 { 270 int ret = -EIO; 271 u8 read_reg = 0; 272 struct platform_device *pdev = to_platform_device(dev); 273 274 if (pdev->id) { 275 /* TWL6030 provide's Card detect support for 276 * only MMC1 controller. 277 */ 278 pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__); 279 return ret; 280 } 281 /* 282 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1 283 * 0 - Card not present ,1 - Card present 284 */ 285 ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg, 286 TWL6030_MMCCTRL); 287 if (ret >= 0) 288 ret = read_reg & STS_MMC; 289 return ret; 290 } 291 EXPORT_SYMBOL(twl6030_mmc_card_detect); 292 293 int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) 294 { 295 296 int status = 0; 297 int i; 298 struct task_struct *task; 299 int ret; 300 u8 mask[4]; 301 302 static struct irq_chip twl6030_irq_chip; 303 mask[1] = 0xFF; 304 mask[2] = 0xFF; 305 mask[3] = 0xFF; 306 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], 307 REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */ 308 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], 309 REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */ 310 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], 311 REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */ 312 313 twl6030_irq_base = irq_base; 314 315 /* install an irq handler for each of the modules; 316 * clone dummy irq_chip since PIH can't *do* anything 317 */ 318 twl6030_irq_chip = dummy_irq_chip; 319 twl6030_irq_chip.name = "twl6030"; 320 twl6030_irq_chip.irq_set_type = NULL; 321 322 for (i = irq_base; i < irq_end; i++) { 323 irq_set_chip_and_handler(i, &twl6030_irq_chip, 324 handle_simple_irq); 325 activate_irq(i); 326 } 327 328 twl6030_irq_next = i; 329 pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", 330 irq_num, irq_base, twl6030_irq_next - 1); 331 332 /* install an irq handler to demultiplex the TWL6030 interrupt */ 333 init_completion(&irq_event); 334 task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); 335 if (IS_ERR(task)) { 336 pr_err("twl6030: could not create irq %d thread!\n", irq_num); 337 status = PTR_ERR(task); 338 goto fail_kthread; 339 } 340 341 status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED, 342 "TWL6030-PIH", &irq_event); 343 if (status < 0) { 344 pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); 345 goto fail_irq; 346 } 347 return status; 348 fail_irq: 349 free_irq(irq_num, &irq_event); 350 351 fail_kthread: 352 for (i = irq_base; i < irq_end; i++) 353 irq_set_chip_and_handler(i, NULL, NULL); 354 return status; 355 } 356 357 int twl6030_exit_irq(void) 358 { 359 360 if (twl6030_irq_base) { 361 pr_err("twl6030: can't yet clean up IRQs?\n"); 362 return -ENOSYS; 363 } 364 return 0; 365 } 366 367