diff options
author | Arnaud Mandy <ext-arnaud.2.mandy@nokia.com> | 2009-12-28 06:40:40 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-02 17:53:47 -0500 |
commit | 1c25fda4a09e8229800979986ef399401053b46e (patch) | |
tree | f9a3282e5a787f89c457978ea7a83815e6c9147f /drivers/usb/musb | |
parent | 1ca9e9ca314c4757409a7f4e9c1f12229a175834 (diff) |
usb: musb: handle irqs in the order dictated by programming guide
MUSB's programming guide dictates how we should handle its
irqs and in which order. Follow that.
Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@nokia.com>
Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r-- | drivers/usb/musb/musb_core.c | 255 |
1 files changed, 116 insertions, 139 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index c4893267b4e2..bd14e816df93 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c | |||
@@ -557,6 +557,69 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, | |||
557 | handled = IRQ_HANDLED; | 557 | handled = IRQ_HANDLED; |
558 | } | 558 | } |
559 | 559 | ||
560 | |||
561 | if (int_usb & MUSB_INTR_SUSPEND) { | ||
562 | DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", | ||
563 | otg_state_string(musb), devctl, power); | ||
564 | handled = IRQ_HANDLED; | ||
565 | |||
566 | switch (musb->xceiv->state) { | ||
567 | #ifdef CONFIG_USB_MUSB_OTG | ||
568 | case OTG_STATE_A_PERIPHERAL: | ||
569 | /* We also come here if the cable is removed, since | ||
570 | * this silicon doesn't report ID-no-longer-grounded. | ||
571 | * | ||
572 | * We depend on T(a_wait_bcon) to shut us down, and | ||
573 | * hope users don't do anything dicey during this | ||
574 | * undesired detour through A_WAIT_BCON. | ||
575 | */ | ||
576 | musb_hnp_stop(musb); | ||
577 | usb_hcd_resume_root_hub(musb_to_hcd(musb)); | ||
578 | musb_root_disconnect(musb); | ||
579 | musb_platform_try_idle(musb, jiffies | ||
580 | + msecs_to_jiffies(musb->a_wait_bcon | ||
581 | ? : OTG_TIME_A_WAIT_BCON)); | ||
582 | |||
583 | break; | ||
584 | #endif | ||
585 | case OTG_STATE_B_IDLE: | ||
586 | if (!musb->is_active) | ||
587 | break; | ||
588 | case OTG_STATE_B_PERIPHERAL: | ||
589 | musb_g_suspend(musb); | ||
590 | musb->is_active = is_otg_enabled(musb) | ||
591 | && musb->xceiv->gadget->b_hnp_enable; | ||
592 | if (musb->is_active) { | ||
593 | #ifdef CONFIG_USB_MUSB_OTG | ||
594 | musb->xceiv->state = OTG_STATE_B_WAIT_ACON; | ||
595 | DBG(1, "HNP: Setting timer for b_ase0_brst\n"); | ||
596 | mod_timer(&musb->otg_timer, jiffies | ||
597 | + msecs_to_jiffies( | ||
598 | OTG_TIME_B_ASE0_BRST)); | ||
599 | #endif | ||
600 | } | ||
601 | break; | ||
602 | case OTG_STATE_A_WAIT_BCON: | ||
603 | if (musb->a_wait_bcon != 0) | ||
604 | musb_platform_try_idle(musb, jiffies | ||
605 | + msecs_to_jiffies(musb->a_wait_bcon)); | ||
606 | break; | ||
607 | case OTG_STATE_A_HOST: | ||
608 | musb->xceiv->state = OTG_STATE_A_SUSPEND; | ||
609 | musb->is_active = is_otg_enabled(musb) | ||
610 | && musb->xceiv->host->b_hnp_enable; | ||
611 | break; | ||
612 | case OTG_STATE_B_HOST: | ||
613 | /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ | ||
614 | DBG(1, "REVISIT: SUSPEND as B_HOST\n"); | ||
615 | break; | ||
616 | default: | ||
617 | /* "should not happen" */ | ||
618 | musb->is_active = 0; | ||
619 | break; | ||
620 | } | ||
621 | } | ||
622 | |||
560 | if (int_usb & MUSB_INTR_CONNECT) { | 623 | if (int_usb & MUSB_INTR_CONNECT) { |
561 | struct usb_hcd *hcd = musb_to_hcd(musb); | 624 | struct usb_hcd *hcd = musb_to_hcd(musb); |
562 | 625 | ||
@@ -625,10 +688,61 @@ b_host: | |||
625 | } | 688 | } |
626 | #endif /* CONFIG_USB_MUSB_HDRC_HCD */ | 689 | #endif /* CONFIG_USB_MUSB_HDRC_HCD */ |
627 | 690 | ||
691 | if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { | ||
692 | DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", | ||
693 | otg_state_string(musb), | ||
694 | MUSB_MODE(musb), devctl); | ||
695 | handled = IRQ_HANDLED; | ||
696 | |||
697 | switch (musb->xceiv->state) { | ||
698 | #ifdef CONFIG_USB_MUSB_HDRC_HCD | ||
699 | case OTG_STATE_A_HOST: | ||
700 | case OTG_STATE_A_SUSPEND: | ||
701 | usb_hcd_resume_root_hub(musb_to_hcd(musb)); | ||
702 | musb_root_disconnect(musb); | ||
703 | if (musb->a_wait_bcon != 0 && is_otg_enabled(musb)) | ||
704 | musb_platform_try_idle(musb, jiffies | ||
705 | + msecs_to_jiffies(musb->a_wait_bcon)); | ||
706 | break; | ||
707 | #endif /* HOST */ | ||
708 | #ifdef CONFIG_USB_MUSB_OTG | ||
709 | case OTG_STATE_B_HOST: | ||
710 | /* REVISIT this behaves for "real disconnect" | ||
711 | * cases; make sure the other transitions from | ||
712 | * from B_HOST act right too. The B_HOST code | ||
713 | * in hnp_stop() is currently not used... | ||
714 | */ | ||
715 | musb_root_disconnect(musb); | ||
716 | musb_to_hcd(musb)->self.is_b_host = 0; | ||
717 | musb->xceiv->state = OTG_STATE_B_PERIPHERAL; | ||
718 | MUSB_DEV_MODE(musb); | ||
719 | musb_g_disconnect(musb); | ||
720 | break; | ||
721 | case OTG_STATE_A_PERIPHERAL: | ||
722 | musb_hnp_stop(musb); | ||
723 | musb_root_disconnect(musb); | ||
724 | /* FALLTHROUGH */ | ||
725 | case OTG_STATE_B_WAIT_ACON: | ||
726 | /* FALLTHROUGH */ | ||
727 | #endif /* OTG */ | ||
728 | #ifdef CONFIG_USB_GADGET_MUSB_HDRC | ||
729 | case OTG_STATE_B_PERIPHERAL: | ||
730 | case OTG_STATE_B_IDLE: | ||
731 | musb_g_disconnect(musb); | ||
732 | break; | ||
733 | #endif /* GADGET */ | ||
734 | default: | ||
735 | WARNING("unhandled DISCONNECT transition (%s)\n", | ||
736 | otg_state_string(musb)); | ||
737 | break; | ||
738 | } | ||
739 | } | ||
740 | |||
628 | /* mentor saves a bit: bus reset and babble share the same irq. | 741 | /* mentor saves a bit: bus reset and babble share the same irq. |
629 | * only host sees babble; only peripheral sees bus reset. | 742 | * only host sees babble; only peripheral sees bus reset. |
630 | */ | 743 | */ |
631 | if (int_usb & MUSB_INTR_RESET) { | 744 | if (int_usb & MUSB_INTR_RESET) { |
745 | handled = IRQ_HANDLED; | ||
632 | if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) { | 746 | if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) { |
633 | /* | 747 | /* |
634 | * Looks like non-HS BABBLE can be ignored, but | 748 | * Looks like non-HS BABBLE can be ignored, but |
@@ -641,7 +755,7 @@ b_host: | |||
641 | DBG(1, "BABBLE devctl: %02x\n", devctl); | 755 | DBG(1, "BABBLE devctl: %02x\n", devctl); |
642 | else { | 756 | else { |
643 | ERR("Stopping host session -- babble\n"); | 757 | ERR("Stopping host session -- babble\n"); |
644 | musb_writeb(mbase, MUSB_DEVCTL, 0); | 758 | musb_writeb(musb->mregs, MUSB_DEVCTL, 0); |
645 | } | 759 | } |
646 | } else if (is_peripheral_capable()) { | 760 | } else if (is_peripheral_capable()) { |
647 | DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); | 761 | DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); |
@@ -686,29 +800,7 @@ b_host: | |||
686 | otg_state_string(musb)); | 800 | otg_state_string(musb)); |
687 | } | 801 | } |
688 | } | 802 | } |
689 | |||
690 | handled = IRQ_HANDLED; | ||
691 | } | 803 | } |
692 | schedule_work(&musb->irq_work); | ||
693 | |||
694 | return handled; | ||
695 | } | ||
696 | |||
697 | /* | ||
698 | * Interrupt Service Routine to record USB "global" interrupts. | ||
699 | * Since these do not happen often and signify things of | ||
700 | * paramount importance, it seems OK to check them individually; | ||
701 | * the order of the tests is specified in the manual | ||
702 | * | ||
703 | * @param musb instance pointer | ||
704 | * @param int_usb register contents | ||
705 | * @param devctl | ||
706 | * @param power | ||
707 | */ | ||
708 | static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, | ||
709 | u8 devctl, u8 power) | ||
710 | { | ||
711 | irqreturn_t handled = IRQ_NONE; | ||
712 | 804 | ||
713 | #if 0 | 805 | #if 0 |
714 | /* REVISIT ... this would be for multiplexing periodic endpoints, or | 806 | /* REVISIT ... this would be for multiplexing periodic endpoints, or |
@@ -755,117 +847,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, | |||
755 | } | 847 | } |
756 | #endif | 848 | #endif |
757 | 849 | ||
758 | if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { | 850 | schedule_work(&musb->irq_work); |
759 | DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", | ||
760 | otg_state_string(musb), | ||
761 | MUSB_MODE(musb), devctl); | ||
762 | handled = IRQ_HANDLED; | ||
763 | |||
764 | switch (musb->xceiv->state) { | ||
765 | #ifdef CONFIG_USB_MUSB_HDRC_HCD | ||
766 | case OTG_STATE_A_HOST: | ||
767 | case OTG_STATE_A_SUSPEND: | ||
768 | usb_hcd_resume_root_hub(musb_to_hcd(musb)); | ||
769 | musb_root_disconnect(musb); | ||
770 | if (musb->a_wait_bcon != 0 && is_otg_enabled(musb)) | ||
771 | musb_platform_try_idle(musb, jiffies | ||
772 | + msecs_to_jiffies(musb->a_wait_bcon)); | ||
773 | break; | ||
774 | #endif /* HOST */ | ||
775 | #ifdef CONFIG_USB_MUSB_OTG | ||
776 | case OTG_STATE_B_HOST: | ||
777 | /* REVISIT this behaves for "real disconnect" | ||
778 | * cases; make sure the other transitions from | ||
779 | * from B_HOST act right too. The B_HOST code | ||
780 | * in hnp_stop() is currently not used... | ||
781 | */ | ||
782 | musb_root_disconnect(musb); | ||
783 | musb_to_hcd(musb)->self.is_b_host = 0; | ||
784 | musb->xceiv->state = OTG_STATE_B_PERIPHERAL; | ||
785 | MUSB_DEV_MODE(musb); | ||
786 | musb_g_disconnect(musb); | ||
787 | break; | ||
788 | case OTG_STATE_A_PERIPHERAL: | ||
789 | musb_hnp_stop(musb); | ||
790 | musb_root_disconnect(musb); | ||
791 | /* FALLTHROUGH */ | ||
792 | case OTG_STATE_B_WAIT_ACON: | ||
793 | /* FALLTHROUGH */ | ||
794 | #endif /* OTG */ | ||
795 | #ifdef CONFIG_USB_GADGET_MUSB_HDRC | ||
796 | case OTG_STATE_B_PERIPHERAL: | ||
797 | case OTG_STATE_B_IDLE: | ||
798 | musb_g_disconnect(musb); | ||
799 | break; | ||
800 | #endif /* GADGET */ | ||
801 | default: | ||
802 | WARNING("unhandled DISCONNECT transition (%s)\n", | ||
803 | otg_state_string(musb)); | ||
804 | break; | ||
805 | } | ||
806 | |||
807 | schedule_work(&musb->irq_work); | ||
808 | } | ||
809 | |||
810 | if (int_usb & MUSB_INTR_SUSPEND) { | ||
811 | DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", | ||
812 | otg_state_string(musb), devctl, power); | ||
813 | handled = IRQ_HANDLED; | ||
814 | |||
815 | switch (musb->xceiv->state) { | ||
816 | #ifdef CONFIG_USB_MUSB_OTG | ||
817 | case OTG_STATE_A_PERIPHERAL: | ||
818 | /* We also come here if the cable is removed, since | ||
819 | * this silicon doesn't report ID-no-longer-grounded. | ||
820 | * | ||
821 | * We depend on T(a_wait_bcon) to shut us down, and | ||
822 | * hope users don't do anything dicey during this | ||
823 | * undesired detour through A_WAIT_BCON. | ||
824 | */ | ||
825 | musb_hnp_stop(musb); | ||
826 | usb_hcd_resume_root_hub(musb_to_hcd(musb)); | ||
827 | musb_root_disconnect(musb); | ||
828 | musb_platform_try_idle(musb, jiffies | ||
829 | + msecs_to_jiffies(musb->a_wait_bcon | ||
830 | ? : OTG_TIME_A_WAIT_BCON)); | ||
831 | break; | ||
832 | #endif | ||
833 | case OTG_STATE_B_PERIPHERAL: | ||
834 | musb_g_suspend(musb); | ||
835 | musb->is_active = is_otg_enabled(musb) | ||
836 | && musb->xceiv->gadget->b_hnp_enable; | ||
837 | if (musb->is_active) { | ||
838 | #ifdef CONFIG_USB_MUSB_OTG | ||
839 | musb->xceiv->state = OTG_STATE_B_WAIT_ACON; | ||
840 | DBG(1, "HNP: Setting timer for b_ase0_brst\n"); | ||
841 | mod_timer(&musb->otg_timer, jiffies | ||
842 | + msecs_to_jiffies( | ||
843 | OTG_TIME_B_ASE0_BRST)); | ||
844 | #endif | ||
845 | } | ||
846 | break; | ||
847 | case OTG_STATE_A_WAIT_BCON: | ||
848 | if (musb->a_wait_bcon != 0) | ||
849 | musb_platform_try_idle(musb, jiffies | ||
850 | + msecs_to_jiffies(musb->a_wait_bcon)); | ||
851 | break; | ||
852 | case OTG_STATE_A_HOST: | ||
853 | musb->xceiv->state = OTG_STATE_A_SUSPEND; | ||
854 | musb->is_active = is_otg_enabled(musb) | ||
855 | && musb->xceiv->host->b_hnp_enable; | ||
856 | break; | ||
857 | case OTG_STATE_B_HOST: | ||
858 | /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ | ||
859 | DBG(1, "REVISIT: SUSPEND as B_HOST\n"); | ||
860 | break; | ||
861 | default: | ||
862 | /* "should not happen" */ | ||
863 | musb->is_active = 0; | ||
864 | break; | ||
865 | } | ||
866 | schedule_work(&musb->irq_work); | ||
867 | } | ||
868 | |||
869 | 851 | ||
870 | return handled; | 852 | return handled; |
871 | } | 853 | } |
@@ -1597,11 +1579,6 @@ irqreturn_t musb_interrupt(struct musb *musb) | |||
1597 | ep_num++; | 1579 | ep_num++; |
1598 | } | 1580 | } |
1599 | 1581 | ||
1600 | /* finish handling "global" interrupts after handling fifos */ | ||
1601 | if (musb->int_usb) | ||
1602 | retval |= musb_stage2_irq(musb, | ||
1603 | musb->int_usb, devctl, power); | ||
1604 | |||
1605 | return retval; | 1582 | return retval; |
1606 | } | 1583 | } |
1607 | 1584 | ||