diff options
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/twl4030-irq.c | 124 |
1 files changed, 52 insertions, 72 deletions
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index bf62389fc7e..1b9ab2f40d5 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c | |||
@@ -422,8 +422,6 @@ static inline void activate_irq(int irq) | |||
422 | 422 | ||
423 | /*----------------------------------------------------------------------*/ | 423 | /*----------------------------------------------------------------------*/ |
424 | 424 | ||
425 | static struct workqueue_struct *wq; | ||
426 | |||
427 | struct sih_agent { | 425 | struct sih_agent { |
428 | int irq_base; | 426 | int irq_base; |
429 | const struct sih *sih; | 427 | const struct sih *sih; |
@@ -432,68 +430,10 @@ struct sih_agent { | |||
432 | bool imr_change_pending; | 430 | bool imr_change_pending; |
433 | 431 | ||
434 | u32 edge_change; | 432 | u32 edge_change; |
435 | struct work_struct edge_work; | ||
436 | 433 | ||
437 | struct mutex irq_lock; | 434 | struct mutex irq_lock; |
438 | }; | 435 | }; |
439 | 436 | ||
440 | static void twl4030_sih_do_edge(struct work_struct *work) | ||
441 | { | ||
442 | struct sih_agent *agent; | ||
443 | const struct sih *sih; | ||
444 | u8 bytes[6]; | ||
445 | u32 edge_change; | ||
446 | int status; | ||
447 | |||
448 | agent = container_of(work, struct sih_agent, edge_work); | ||
449 | |||
450 | /* see what work we have */ | ||
451 | edge_change = agent->edge_change; | ||
452 | agent->edge_change = 0; | ||
453 | sih = edge_change ? agent->sih : NULL; | ||
454 | if (!sih) | ||
455 | return; | ||
456 | |||
457 | /* Read, reserving first byte for write scratch. Yes, this | ||
458 | * could be cached for some speedup ... but be careful about | ||
459 | * any processor on the other IRQ line, EDR registers are | ||
460 | * shared. | ||
461 | */ | ||
462 | status = twl_i2c_read(sih->module, bytes + 1, | ||
463 | sih->edr_offset, sih->bytes_edr); | ||
464 | if (status) { | ||
465 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
466 | "read", status); | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | /* Modify only the bits we know must change */ | ||
471 | while (edge_change) { | ||
472 | int i = fls(edge_change) - 1; | ||
473 | struct irq_data *idata = irq_get_irq_data(i + agent->irq_base); | ||
474 | int byte = 1 + (i >> 2); | ||
475 | int off = (i & 0x3) * 2; | ||
476 | unsigned int type; | ||
477 | |||
478 | bytes[byte] &= ~(0x03 << off); | ||
479 | |||
480 | type = irqd_get_trigger_type(idata); | ||
481 | if (type & IRQ_TYPE_EDGE_RISING) | ||
482 | bytes[byte] |= BIT(off + 1); | ||
483 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
484 | bytes[byte] |= BIT(off + 0); | ||
485 | |||
486 | edge_change &= ~BIT(i); | ||
487 | } | ||
488 | |||
489 | /* Write */ | ||
490 | status = twl_i2c_write(sih->module, bytes, | ||
491 | sih->edr_offset, sih->bytes_edr); | ||
492 | if (status) | ||
493 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
494 | "write", status); | ||
495 | } | ||
496 | |||
497 | /*----------------------------------------------------------------------*/ | 437 | /*----------------------------------------------------------------------*/ |
498 | 438 | ||
499 | /* | 439 | /* |
@@ -526,10 +466,8 @@ static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) | |||
526 | if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) | 466 | if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) |
527 | return -EINVAL; | 467 | return -EINVAL; |
528 | 468 | ||
529 | if (irqd_get_trigger_type(data) != trigger) { | 469 | if (irqd_get_trigger_type(data) != trigger) |
530 | agent->edge_change |= BIT(data->irq - agent->irq_base); | 470 | agent->edge_change |= BIT(data->irq - agent->irq_base); |
531 | queue_work(wq, &agent->edge_work); | ||
532 | } | ||
533 | 471 | ||
534 | return 0; | 472 | return 0; |
535 | } | 473 | } |
@@ -566,6 +504,56 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) | |||
566 | "write", status); | 504 | "write", status); |
567 | } | 505 | } |
568 | 506 | ||
507 | if (agent->edge_change) { | ||
508 | u32 edge_change; | ||
509 | u8 bytes[6]; | ||
510 | |||
511 | edge_change = agent->edge_change; | ||
512 | agent->edge_change = 0; | ||
513 | |||
514 | /* | ||
515 | * Read, reserving first byte for write scratch. Yes, this | ||
516 | * could be cached for some speedup ... but be careful about | ||
517 | * any processor on the other IRQ line, EDR registers are | ||
518 | * shared. | ||
519 | */ | ||
520 | status = twl_i2c_read(sih->module, bytes + 1, | ||
521 | sih->edr_offset, sih->bytes_edr); | ||
522 | if (status) { | ||
523 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
524 | "read", status); | ||
525 | return; | ||
526 | } | ||
527 | |||
528 | /* Modify only the bits we know must change */ | ||
529 | while (edge_change) { | ||
530 | int i = fls(edge_change) - 1; | ||
531 | struct irq_data *idata; | ||
532 | int byte = 1 + (i >> 2); | ||
533 | int off = (i & 0x3) * 2; | ||
534 | unsigned int type; | ||
535 | |||
536 | idata = irq_get_irq_data(i + agent->irq_base); | ||
537 | |||
538 | bytes[byte] &= ~(0x03 << off); | ||
539 | |||
540 | type = irqd_get_trigger_type(idata); | ||
541 | if (type & IRQ_TYPE_EDGE_RISING) | ||
542 | bytes[byte] |= BIT(off + 1); | ||
543 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
544 | bytes[byte] |= BIT(off + 0); | ||
545 | |||
546 | edge_change &= ~BIT(i); | ||
547 | } | ||
548 | |||
549 | /* Write */ | ||
550 | status = twl_i2c_write(sih->module, bytes, | ||
551 | sih->edr_offset, sih->bytes_edr); | ||
552 | if (status) | ||
553 | pr_err("twl4030: %s, %s --> %d\n", __func__, | ||
554 | "write", status); | ||
555 | } | ||
556 | |||
569 | mutex_unlock(&agent->irq_lock); | 557 | mutex_unlock(&agent->irq_lock); |
570 | } | 558 | } |
571 | 559 | ||
@@ -672,7 +660,6 @@ int twl4030_sih_setup(int module) | |||
672 | agent->sih = sih; | 660 | agent->sih = sih; |
673 | agent->imr = ~0; | 661 | agent->imr = ~0; |
674 | mutex_init(&agent->irq_lock); | 662 | mutex_init(&agent->irq_lock); |
675 | INIT_WORK(&agent->edge_work, twl4030_sih_do_edge); | ||
676 | 663 | ||
677 | for (i = 0; i < sih->bits; i++) { | 664 | for (i = 0; i < sih->bits; i++) { |
678 | irq = irq_base + i; | 665 | irq = irq_base + i; |
@@ -720,12 +707,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
720 | if (status < 0) | 707 | if (status < 0) |
721 | return status; | 708 | return status; |
722 | 709 | ||
723 | wq = create_singlethread_workqueue("twl4030-irqchip"); | ||
724 | if (!wq) { | ||
725 | pr_err("twl4030: workqueue FAIL\n"); | ||
726 | return -ESRCH; | ||
727 | } | ||
728 | |||
729 | twl4030_irq_base = irq_base; | 710 | twl4030_irq_base = irq_base; |
730 | 711 | ||
731 | /* install an irq handler for each of the SIH modules; | 712 | /* install an irq handler for each of the SIH modules; |
@@ -766,8 +747,7 @@ fail_rqirq: | |||
766 | fail: | 747 | fail: |
767 | for (i = irq_base; i < irq_end; i++) | 748 | for (i = irq_base; i < irq_end; i++) |
768 | irq_set_chip_and_handler(i, NULL, NULL); | 749 | irq_set_chip_and_handler(i, NULL, NULL); |
769 | destroy_workqueue(wq); | 750 | |
770 | wq = NULL; | ||
771 | return status; | 751 | return status; |
772 | } | 752 | } |
773 | 753 | ||