diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/ab8500-core.c | 108 |
1 files changed, 105 insertions, 3 deletions
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 08850f0d1523..6a59919fc331 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c | |||
@@ -91,6 +91,15 @@ | |||
91 | #define AB8500_IT_MASK23_REG 0x56 | 91 | #define AB8500_IT_MASK23_REG 0x56 |
92 | #define AB8500_IT_MASK24_REG 0x57 | 92 | #define AB8500_IT_MASK24_REG 0x57 |
93 | 93 | ||
94 | /* | ||
95 | * latch hierarchy registers | ||
96 | */ | ||
97 | #define AB8500_IT_LATCHHIER1_REG 0x60 | ||
98 | #define AB8500_IT_LATCHHIER2_REG 0x61 | ||
99 | #define AB8500_IT_LATCHHIER3_REG 0x62 | ||
100 | |||
101 | #define AB8500_IT_LATCHHIER_NUM 3 | ||
102 | |||
94 | #define AB8500_REV_REG 0x80 | 103 | #define AB8500_REV_REG 0x80 |
95 | #define AB8500_IC_NAME_REG 0x82 | 104 | #define AB8500_IC_NAME_REG 0x82 |
96 | #define AB8500_SWITCH_OFF_STATUS 0x00 | 105 | #define AB8500_SWITCH_OFF_STATUS 0x00 |
@@ -340,6 +349,90 @@ static struct irq_chip ab8500_irq_chip = { | |||
340 | .irq_unmask = ab8500_irq_unmask, | 349 | .irq_unmask = ab8500_irq_unmask, |
341 | }; | 350 | }; |
342 | 351 | ||
352 | static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, | ||
353 | int latch_offset, u8 latch_val) | ||
354 | { | ||
355 | int int_bit = __ffs(latch_val); | ||
356 | int line, i; | ||
357 | |||
358 | do { | ||
359 | int_bit = __ffs(latch_val); | ||
360 | |||
361 | for (i = 0; i < ab8500->mask_size; i++) | ||
362 | if (ab8500->irq_reg_offset[i] == latch_offset) | ||
363 | break; | ||
364 | |||
365 | if (i >= ab8500->mask_size) { | ||
366 | dev_err(ab8500->dev, "Register offset 0x%2x not declared\n", | ||
367 | latch_offset); | ||
368 | return -ENXIO; | ||
369 | } | ||
370 | |||
371 | line = (i << 3) + int_bit; | ||
372 | latch_val &= ~(1 << int_bit); | ||
373 | |||
374 | handle_nested_irq(ab8500->irq_base + line); | ||
375 | } while (latch_val); | ||
376 | |||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500, | ||
381 | int hier_offset, u8 hier_val) | ||
382 | { | ||
383 | int latch_bit, status; | ||
384 | u8 latch_offset, latch_val; | ||
385 | |||
386 | do { | ||
387 | latch_bit = __ffs(hier_val); | ||
388 | latch_offset = (hier_offset << 3) + latch_bit; | ||
389 | |||
390 | /* Fix inconsistent ITFromLatch25 bit mapping... */ | ||
391 | if (unlikely(latch_offset == 17)) | ||
392 | latch_offset = 24; | ||
393 | |||
394 | status = get_register_interruptible(ab8500, | ||
395 | AB8500_INTERRUPT, | ||
396 | AB8500_IT_LATCH1_REG + latch_offset, | ||
397 | &latch_val); | ||
398 | if (status < 0 || latch_val == 0) | ||
399 | goto discard; | ||
400 | |||
401 | status = ab8500_handle_hierarchical_line(ab8500, | ||
402 | latch_offset, latch_val); | ||
403 | if (status < 0) | ||
404 | return status; | ||
405 | discard: | ||
406 | hier_val &= ~(1 << latch_bit); | ||
407 | } while (hier_val); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev) | ||
413 | { | ||
414 | struct ab8500 *ab8500 = dev; | ||
415 | u8 i; | ||
416 | |||
417 | dev_vdbg(ab8500->dev, "interrupt\n"); | ||
418 | |||
419 | /* Hierarchical interrupt version */ | ||
420 | for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) { | ||
421 | int status; | ||
422 | u8 hier_val; | ||
423 | |||
424 | status = get_register_interruptible(ab8500, AB8500_INTERRUPT, | ||
425 | AB8500_IT_LATCHHIER1_REG + i, &hier_val); | ||
426 | if (status < 0 || hier_val == 0) | ||
427 | continue; | ||
428 | |||
429 | status = ab8500_handle_hierarchical_latch(ab8500, i, hier_val); | ||
430 | if (status < 0) | ||
431 | break; | ||
432 | } | ||
433 | return IRQ_HANDLED; | ||
434 | } | ||
435 | |||
343 | static irqreturn_t ab8500_irq(int irq, void *dev) | 436 | static irqreturn_t ab8500_irq(int irq, void *dev) |
344 | { | 437 | { |
345 | struct ab8500 *ab8500 = dev; | 438 | struct ab8500 *ab8500 = dev; |
@@ -1179,9 +1272,18 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) | |||
1179 | if (ret) | 1272 | if (ret) |
1180 | goto out_freeoldmask; | 1273 | goto out_freeoldmask; |
1181 | 1274 | ||
1182 | ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, | 1275 | /* Activate this feature only in ab9540 */ |
1183 | IRQF_ONESHOT | IRQF_NO_SUSPEND, | 1276 | /* till tests are done on ab8500 1p2 or later*/ |
1184 | "ab8500", ab8500); | 1277 | if (is_ab9540(ab8500)) |
1278 | ret = request_threaded_irq(ab8500->irq, NULL, | ||
1279 | ab8500_hierarchical_irq, | ||
1280 | IRQF_ONESHOT | IRQF_NO_SUSPEND, | ||
1281 | "ab8500", ab8500); | ||
1282 | else | ||
1283 | ret = request_threaded_irq(ab8500->irq, NULL, | ||
1284 | ab8500_irq, | ||
1285 | IRQF_ONESHOT | IRQF_NO_SUSPEND, | ||
1286 | "ab8500", ab8500); | ||
1185 | if (ret) | 1287 | if (ret) |
1186 | goto out_removeirq; | 1288 | goto out_removeirq; |
1187 | } | 1289 | } |