Blame view
drivers/mfd/wm831x-irq.c
14.4 KB
7d4d0a3e7 mfd: Add WM831x i... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * wm831x-irq.c -- Interrupt controller support for Wolfson WM831x PMICs * * Copyright 2009 Wolfson Microelectronics PLC. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> |
5fb4d38b1 mfd: Move WM831x ... |
18 |
#include <linux/irq.h> |
7d4d0a3e7 mfd: Add WM831x i... |
19 20 21 22 23 |
#include <linux/mfd/core.h> #include <linux/interrupt.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/pdata.h> |
896060c76 ASoC: Use more id... |
24 |
#include <linux/mfd/wm831x/gpio.h> |
7d4d0a3e7 mfd: Add WM831x i... |
25 26 27 |
#include <linux/mfd/wm831x/irq.h> #include <linux/delay.h> |
7d4d0a3e7 mfd: Add WM831x i... |
28 29 30 31 |
struct wm831x_irq_data { int primary; int reg; int mask; |
7d4d0a3e7 mfd: Add WM831x i... |
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
}; static struct wm831x_irq_data wm831x_irqs[] = { [WM831X_IRQ_TEMP_THW] = { .primary = WM831X_TEMP_INT, .reg = 1, .mask = WM831X_TEMP_THW_EINT, }, [WM831X_IRQ_GPIO_1] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP1_EINT, }, [WM831X_IRQ_GPIO_2] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP2_EINT, }, [WM831X_IRQ_GPIO_3] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP3_EINT, }, [WM831X_IRQ_GPIO_4] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP4_EINT, }, [WM831X_IRQ_GPIO_5] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP5_EINT, }, [WM831X_IRQ_GPIO_6] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP6_EINT, }, [WM831X_IRQ_GPIO_7] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP7_EINT, }, [WM831X_IRQ_GPIO_8] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP8_EINT, }, [WM831X_IRQ_GPIO_9] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP9_EINT, }, [WM831X_IRQ_GPIO_10] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP10_EINT, }, [WM831X_IRQ_GPIO_11] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP11_EINT, }, [WM831X_IRQ_GPIO_12] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP12_EINT, }, [WM831X_IRQ_GPIO_13] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP13_EINT, }, [WM831X_IRQ_GPIO_14] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP14_EINT, }, [WM831X_IRQ_GPIO_15] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP15_EINT, }, [WM831X_IRQ_GPIO_16] = { .primary = WM831X_GP_INT, .reg = 5, .mask = WM831X_GP16_EINT, }, [WM831X_IRQ_ON] = { .primary = WM831X_ON_PIN_INT, .reg = 1, .mask = WM831X_ON_PIN_EINT, }, [WM831X_IRQ_PPM_SYSLO] = { .primary = WM831X_PPM_INT, .reg = 1, .mask = WM831X_PPM_SYSLO_EINT, }, [WM831X_IRQ_PPM_PWR_SRC] = { .primary = WM831X_PPM_INT, .reg = 1, .mask = WM831X_PPM_PWR_SRC_EINT, }, [WM831X_IRQ_PPM_USB_CURR] = { .primary = WM831X_PPM_INT, .reg = 1, .mask = WM831X_PPM_USB_CURR_EINT, }, [WM831X_IRQ_WDOG_TO] = { .primary = WM831X_WDOG_INT, .reg = 1, .mask = WM831X_WDOG_TO_EINT, }, [WM831X_IRQ_RTC_PER] = { .primary = WM831X_RTC_INT, .reg = 1, .mask = WM831X_RTC_PER_EINT, }, [WM831X_IRQ_RTC_ALM] = { .primary = WM831X_RTC_INT, .reg = 1, .mask = WM831X_RTC_ALM_EINT, }, [WM831X_IRQ_CHG_BATT_HOT] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_BATT_HOT_EINT, }, [WM831X_IRQ_CHG_BATT_COLD] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_BATT_COLD_EINT, }, [WM831X_IRQ_CHG_BATT_FAIL] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_BATT_FAIL_EINT, }, [WM831X_IRQ_CHG_OV] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_OV_EINT, }, [WM831X_IRQ_CHG_END] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_END_EINT, }, [WM831X_IRQ_CHG_TO] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_TO_EINT, }, [WM831X_IRQ_CHG_MODE] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_MODE_EINT, }, [WM831X_IRQ_CHG_START] = { .primary = WM831X_CHG_INT, .reg = 2, .mask = WM831X_CHG_START_EINT, }, [WM831X_IRQ_TCHDATA] = { .primary = WM831X_TCHDATA_INT, .reg = 1, .mask = WM831X_TCHDATA_EINT, }, [WM831X_IRQ_TCHPD] = { .primary = WM831X_TCHPD_INT, .reg = 1, .mask = WM831X_TCHPD_EINT, }, [WM831X_IRQ_AUXADC_DATA] = { .primary = WM831X_AUXADC_INT, .reg = 1, .mask = WM831X_AUXADC_DATA_EINT, }, [WM831X_IRQ_AUXADC_DCOMP1] = { .primary = WM831X_AUXADC_INT, .reg = 1, .mask = WM831X_AUXADC_DCOMP1_EINT, }, [WM831X_IRQ_AUXADC_DCOMP2] = { .primary = WM831X_AUXADC_INT, .reg = 1, .mask = WM831X_AUXADC_DCOMP2_EINT, }, [WM831X_IRQ_AUXADC_DCOMP3] = { .primary = WM831X_AUXADC_INT, .reg = 1, .mask = WM831X_AUXADC_DCOMP3_EINT, }, [WM831X_IRQ_AUXADC_DCOMP4] = { .primary = WM831X_AUXADC_INT, .reg = 1, .mask = WM831X_AUXADC_DCOMP4_EINT, }, [WM831X_IRQ_CS1] = { .primary = WM831X_CS_INT, .reg = 2, .mask = WM831X_CS1_EINT, }, [WM831X_IRQ_CS2] = { .primary = WM831X_CS_INT, .reg = 2, .mask = WM831X_CS2_EINT, }, [WM831X_IRQ_HC_DC1] = { .primary = WM831X_HC_INT, .reg = 4, .mask = WM831X_HC_DC1_EINT, }, [WM831X_IRQ_HC_DC2] = { .primary = WM831X_HC_INT, .reg = 4, .mask = WM831X_HC_DC2_EINT, }, [WM831X_IRQ_UV_LDO1] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO1_EINT, }, [WM831X_IRQ_UV_LDO2] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO2_EINT, }, [WM831X_IRQ_UV_LDO3] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO3_EINT, }, [WM831X_IRQ_UV_LDO4] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO4_EINT, }, [WM831X_IRQ_UV_LDO5] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO5_EINT, }, [WM831X_IRQ_UV_LDO6] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO6_EINT, }, [WM831X_IRQ_UV_LDO7] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO7_EINT, }, [WM831X_IRQ_UV_LDO8] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO8_EINT, }, [WM831X_IRQ_UV_LDO9] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO9_EINT, }, [WM831X_IRQ_UV_LDO10] = { .primary = WM831X_UV_INT, .reg = 3, .mask = WM831X_UV_LDO10_EINT, }, [WM831X_IRQ_UV_DC1] = { .primary = WM831X_UV_INT, .reg = 4, .mask = WM831X_UV_DC1_EINT, }, [WM831X_IRQ_UV_DC2] = { .primary = WM831X_UV_INT, .reg = 4, .mask = WM831X_UV_DC2_EINT, }, [WM831X_IRQ_UV_DC3] = { .primary = WM831X_UV_INT, .reg = 4, .mask = WM831X_UV_DC3_EINT, }, [WM831X_IRQ_UV_DC4] = { .primary = WM831X_UV_INT, .reg = 4, .mask = WM831X_UV_DC4_EINT, }, }; static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data) { return WM831X_INTERRUPT_STATUS_1 - 1 + irq_data->reg; } |
5fb4d38b1 mfd: Move WM831x ... |
326 327 |
static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x, int irq) |
7d4d0a3e7 mfd: Add WM831x i... |
328 |
{ |
5fb4d38b1 mfd: Move WM831x ... |
329 |
return &wm831x_irqs[irq - wm831x->irq_base]; |
7d4d0a3e7 mfd: Add WM831x i... |
330 |
} |
ba81cd393 mfd: Convert WM83... |
331 |
static void wm831x_irq_lock(struct irq_data *data) |
7d4d0a3e7 mfd: Add WM831x i... |
332 |
{ |
25a947f80 mfd: Convert Wolf... |
333 |
struct wm831x *wm831x = irq_data_get_irq_chip_data(data); |
7d4d0a3e7 mfd: Add WM831x i... |
334 |
|
7d4d0a3e7 mfd: Add WM831x i... |
335 |
mutex_lock(&wm831x->irq_lock); |
7d4d0a3e7 mfd: Add WM831x i... |
336 |
} |
7d4d0a3e7 mfd: Add WM831x i... |
337 |
|
ba81cd393 mfd: Convert WM83... |
338 |
static void wm831x_irq_sync_unlock(struct irq_data *data) |
7d4d0a3e7 mfd: Add WM831x i... |
339 |
{ |
25a947f80 mfd: Convert Wolf... |
340 |
struct wm831x *wm831x = irq_data_get_irq_chip_data(data); |
5fb4d38b1 mfd: Move WM831x ... |
341 |
int i; |
ca7a71824 mfd: Fix bus lock... |
342 343 344 345 346 347 348 349 |
for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) { if (wm831x->gpio_update[i]) { wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i, WM831X_GPN_INT_MODE | WM831X_GPN_POL, wm831x->gpio_update[i]); wm831x->gpio_update[i] = 0; } } |
5fb4d38b1 mfd: Move WM831x ... |
350 351 352 353 |
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { /* If there's been a change in the mask write it back * to the hardware. */ if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) { |
f624effb7 mfd: Convert WM83... |
354 355 356 357 |
dev_dbg(wm831x->dev, "IRQ mask sync: %x = %x ", WM831X_INTERRUPT_STATUS_1_MASK + i, wm831x->irq_masks_cur[i]); |
5fb4d38b1 mfd: Move WM831x ... |
358 359 360 361 362 |
wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i]; wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, wm831x->irq_masks_cur[i]); } |
7d4d0a3e7 mfd: Add WM831x i... |
363 |
} |
7d4d0a3e7 mfd: Add WM831x i... |
364 |
mutex_unlock(&wm831x->irq_lock); |
7d4d0a3e7 mfd: Add WM831x i... |
365 |
} |
7d4d0a3e7 mfd: Add WM831x i... |
366 |
|
f624effb7 mfd: Convert WM83... |
367 |
static void wm831x_irq_enable(struct irq_data *data) |
7d4d0a3e7 mfd: Add WM831x i... |
368 |
{ |
25a947f80 mfd: Convert Wolf... |
369 |
struct wm831x *wm831x = irq_data_get_irq_chip_data(data); |
ba81cd393 mfd: Convert WM83... |
370 371 |
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, data->irq); |
7d4d0a3e7 mfd: Add WM831x i... |
372 |
|
5fb4d38b1 mfd: Move WM831x ... |
373 |
wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; |
7d4d0a3e7 mfd: Add WM831x i... |
374 |
} |
7d4d0a3e7 mfd: Add WM831x i... |
375 |
|
f624effb7 mfd: Convert WM83... |
376 |
static void wm831x_irq_disable(struct irq_data *data) |
7d4d0a3e7 mfd: Add WM831x i... |
377 |
{ |
25a947f80 mfd: Convert Wolf... |
378 |
struct wm831x *wm831x = irq_data_get_irq_chip_data(data); |
ba81cd393 mfd: Convert WM83... |
379 380 |
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, data->irq); |
5fb4d38b1 mfd: Move WM831x ... |
381 382 |
wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; |
7d4d0a3e7 mfd: Add WM831x i... |
383 |
} |
ba81cd393 mfd: Convert WM83... |
384 |
static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) |
896060c76 ASoC: Use more id... |
385 |
{ |
25a947f80 mfd: Convert Wolf... |
386 |
struct wm831x *wm831x = irq_data_get_irq_chip_data(data); |
ca7a71824 mfd: Fix bus lock... |
387 |
int irq; |
896060c76 ASoC: Use more id... |
388 |
|
ba81cd393 mfd: Convert WM83... |
389 |
irq = data->irq - wm831x->irq_base; |
896060c76 ASoC: Use more id... |
390 |
|
c9d66d351 mfd: Ignore non-G... |
391 392 393 394 395 396 397 |
if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) { /* Ignore internal-only IRQs */ if (irq >= 0 && irq < WM831X_NUM_IRQS) return 0; else return -EINVAL; } |
896060c76 ASoC: Use more id... |
398 |
|
08256712a mfd: Fix off by o... |
399 400 401 402 |
/* Rebase the IRQ into the GPIO range so we've got a sensible array * index. */ irq -= WM831X_IRQ_GPIO_1; |
ca7a71824 mfd: Fix bus lock... |
403 404 405 406 |
/* We set the high bit to flag that we need an update; don't * do the update here as we can be called with the bus lock * held. */ |
896060c76 ASoC: Use more id... |
407 408 |
switch (type) { case IRQ_TYPE_EDGE_BOTH: |
ca7a71824 mfd: Fix bus lock... |
409 |
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; |
7583a213e mfd: Simulate act... |
410 |
wm831x->gpio_level[irq] = false; |
896060c76 ASoC: Use more id... |
411 412 |
break; case IRQ_TYPE_EDGE_RISING: |
ca7a71824 mfd: Fix bus lock... |
413 |
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; |
7583a213e mfd: Simulate act... |
414 |
wm831x->gpio_level[irq] = false; |
896060c76 ASoC: Use more id... |
415 416 |
break; case IRQ_TYPE_EDGE_FALLING: |
ca7a71824 mfd: Fix bus lock... |
417 |
wm831x->gpio_update[irq] = 0x10000; |
7583a213e mfd: Simulate act... |
418 419 420 421 422 |
wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_LEVEL_HIGH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; wm831x->gpio_level[irq] = true; |
896060c76 ASoC: Use more id... |
423 424 425 426 |
break; default: return -EINVAL; } |
ca7a71824 mfd: Fix bus lock... |
427 |
return 0; |
896060c76 ASoC: Use more id... |
428 |
} |
5fb4d38b1 mfd: Move WM831x ... |
429 |
static struct irq_chip wm831x_irq_chip = { |
ba81cd393 mfd: Convert WM83... |
430 431 432 |
.name = "wm831x", .irq_bus_lock = wm831x_irq_lock, .irq_bus_sync_unlock = wm831x_irq_sync_unlock, |
f624effb7 mfd: Convert WM83... |
433 434 |
.irq_disable = wm831x_irq_disable, .irq_enable = wm831x_irq_enable, |
ba81cd393 mfd: Convert WM83... |
435 |
.irq_set_type = wm831x_irq_set_type, |
5fb4d38b1 mfd: Move WM831x ... |
436 437 438 439 440 |
}; /* The processing of the primary interrupt occurs in a thread so that * we can interact with the device over I2C or SPI. */ static irqreturn_t wm831x_irq_thread(int irq, void *data) |
7d4d0a3e7 mfd: Add WM831x i... |
441 |
{ |
5fb4d38b1 mfd: Move WM831x ... |
442 |
struct wm831x *wm831x = data; |
7d4d0a3e7 mfd: Add WM831x i... |
443 |
unsigned int i; |
7583a213e mfd: Simulate act... |
444 |
int primary, status_addr, ret; |
5fb4d38b1 mfd: Move WM831x ... |
445 446 |
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 }; int read[WM831X_NUM_IRQ_REGS] = { 0 }; |
7d4d0a3e7 mfd: Add WM831x i... |
447 448 449 450 451 452 453 454 455 |
int *status; primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS); if (primary < 0) { dev_err(wm831x->dev, "Failed to read system interrupt: %d ", primary); goto out; } |
8546bd4af mfd: Add fast pat... |
456 457 458 459 460 461 462 463 464 |
/* The touch interrupts are visible in the primary register as * an optimisation; open code this to avoid complicating the * main handling loop and so we can also skip iterating the * descriptors. */ if (primary & WM831X_TCHPD_INT) handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD); if (primary & WM831X_TCHDATA_INT) handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA); |
953c7d025 mfd: Still check ... |
465 |
primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT); |
8546bd4af mfd: Add fast pat... |
466 |
|
7d4d0a3e7 mfd: Add WM831x i... |
467 468 469 470 471 472 473 474 475 476 477 |
for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) { int offset = wm831x_irqs[i].reg - 1; if (!(primary & wm831x_irqs[i].primary)) continue; status = &status_regs[offset]; /* Hopefully there should only be one register to read * each time otherwise we ought to do a block read. */ if (!read[offset]) { |
88c939770 mfd: Acknowlege a... |
478 479 480 |
status_addr = irq_data_to_status_reg(&wm831x_irqs[i]); *status = wm831x_reg_read(wm831x, status_addr); |
7d4d0a3e7 mfd: Add WM831x i... |
481 482 483 484 485 |
if (*status < 0) { dev_err(wm831x->dev, "Failed to read IRQ status: %d ", *status); |
5fb4d38b1 mfd: Move WM831x ... |
486 |
goto out; |
7d4d0a3e7 mfd: Add WM831x i... |
487 |
} |
7d4d0a3e7 mfd: Add WM831x i... |
488 |
read[offset] = 1; |
88c939770 mfd: Acknowlege a... |
489 490 491 492 493 494 495 496 |
/* Ignore any bits that we don't think are masked */ *status &= ~wm831x->irq_masks_cur[offset]; /* Acknowledge now so we don't miss * notifications while we handle. */ wm831x_reg_write(wm831x, status_addr, *status); |
7d4d0a3e7 mfd: Add WM831x i... |
497 |
} |
88c939770 mfd: Acknowlege a... |
498 |
if (*status & wm831x_irqs[i].mask) |
5fb4d38b1 mfd: Move WM831x ... |
499 |
handle_nested_irq(wm831x->irq_base + i); |
7583a213e mfd: Simulate act... |
500 501 502 503 504 505 506 507 508 509 510 511 512 |
/* Simulate an edge triggered IRQ by polling the input * status. This is sucky but improves interoperability. */ if (primary == WM831X_GP_INT && wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) { ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) { handle_nested_irq(wm831x->irq_base + i); ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); } } |
7d4d0a3e7 mfd: Add WM831x i... |
513 |
} |
7d4d0a3e7 mfd: Add WM831x i... |
514 |
out: |
7d4d0a3e7 mfd: Add WM831x i... |
515 516 517 518 519 |
return IRQ_HANDLED; } int wm831x_irq_init(struct wm831x *wm831x, int irq) { |
5fb4d38b1 mfd: Move WM831x ... |
520 521 |
struct wm831x_pdata *pdata = wm831x->dev->platform_data; int i, cur_irq, ret; |
7d4d0a3e7 mfd: Add WM831x i... |
522 |
|
14f572fa3 mfd: Always initi... |
523 |
mutex_init(&wm831x->irq_lock); |
0d7e0e399 mfd: Initialise W... |
524 525 526 527 528 529 530 |
/* Mask the individual interrupt sources */ for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { wm831x->irq_masks_cur[i] = 0xffff; wm831x->irq_masks_cache[i] = 0xffff; wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, 0xffff); } |
5c05a8d1f mfd: Support dyna... |
531 532 533 534 535 536 537 538 539 540 541 542 543 |
/* Try to dynamically allocate IRQs if no base is specified */ if (!pdata || !pdata->irq_base) wm831x->irq_base = -1; else wm831x->irq_base = pdata->irq_base; wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0, WM831X_NUM_IRQS, 0); if (wm831x->irq_base < 0) { dev_warn(wm831x->dev, "Failed to allocate IRQs: %d ", wm831x->irq_base); wm831x->irq_base = 0; |
5fb4d38b1 mfd: Move WM831x ... |
544 |
return 0; |
7d4d0a3e7 mfd: Add WM831x i... |
545 |
} |
5c05a8d1f mfd: Support dyna... |
546 |
if (pdata && pdata->irq_cmos) |
b103e0b3c mfd: Support conf... |
547 548 549 550 551 552 |
i = 0; else i = WM831X_IRQ_OD; wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, WM831X_IRQ_OD, i); |
180e4f5f2 mfd: Flag WM831x ... |
553 554 555 556 557 558 559 560 561 562 563 |
/* Try to flag /IRQ as a wake source; there are a number of * unconditional wake sources in the PMIC so this isn't * conditional but we don't actually care *too* much if it * fails. */ ret = enable_irq_wake(irq); if (ret != 0) { dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d ", ret); } |
7d4d0a3e7 mfd: Add WM831x i... |
564 |
wm831x->irq = irq; |
7d4d0a3e7 mfd: Add WM831x i... |
565 |
|
5fb4d38b1 mfd: Move WM831x ... |
566 567 568 569 |
/* Register them with genirq */ for (cur_irq = wm831x->irq_base; cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base; cur_irq++) { |
d5bb12216 mfd: Cleanup irq ... |
570 571 |
irq_set_chip_data(cur_irq, wm831x); irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip, |
5fb4d38b1 mfd: Move WM831x ... |
572 |
handle_edge_irq); |
d5bb12216 mfd: Cleanup irq ... |
573 |
irq_set_nested_thread(cur_irq, 1); |
5fb4d38b1 mfd: Move WM831x ... |
574 575 576 577 578 579 |
/* ARM needs us to explicitly flag the IRQ as valid * and will set them noprobe when we do so. */ #ifdef CONFIG_ARM set_irq_flags(cur_irq, IRQF_VALID); #else |
d5bb12216 mfd: Cleanup irq ... |
580 |
irq_set_noprobe(cur_irq); |
5fb4d38b1 mfd: Move WM831x ... |
581 582 |
#endif } |
7d4d0a3e7 mfd: Add WM831x i... |
583 |
|
bc86fcee3 mfd: Continue wit... |
584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
if (irq) { ret = request_threaded_irq(irq, NULL, wm831x_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "wm831x", wm831x); if (ret != 0) { dev_err(wm831x->dev, "Failed to request IRQ %d: %d ", irq, ret); return ret; } } else { dev_warn(wm831x->dev, "No interrupt specified - functionality limited "); |
7d4d0a3e7 mfd: Add WM831x i... |
598 |
} |
5fb4d38b1 mfd: Move WM831x ... |
599 600 |
/* Enable top level interrupts, we mask at secondary level */ wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0); |
7d4d0a3e7 mfd: Add WM831x i... |
601 602 603 604 605 606 607 608 |
return 0; } void wm831x_irq_exit(struct wm831x *wm831x) { if (wm831x->irq) free_irq(wm831x->irq, wm831x); } |