aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r--drivers/usb/otg/msm_otg.c380
1 files changed, 376 insertions, 4 deletions
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 7792fef0be5e..854b7e3413dd 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1,4 +1,4 @@
1/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. 1/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
2 * 2 *
3 * This program is free software; you can redistribute it and/or modify 3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and 4 * it under the terms of the GNU General Public License version 2 and
@@ -409,6 +409,33 @@ skip_phy_resume:
409} 409}
410#endif 410#endif
411 411
412static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
413{
414 if (motg->cur_power == mA)
415 return;
416
417 /* TODO: Notify PMIC about available current */
418 dev_info(motg->otg.dev, "Avail curr from USB = %u\n", mA);
419 motg->cur_power = mA;
420}
421
422static int msm_otg_set_power(struct otg_transceiver *otg, unsigned mA)
423{
424 struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
425
426 /*
427 * Gadget driver uses set_power method to notify about the
428 * available current based on suspend/configured states.
429 *
430 * IDEV_CHG can be drawn irrespective of suspend/un-configured
431 * states when CDP/ACA is connected.
432 */
433 if (motg->chg_type == USB_SDP_CHARGER)
434 msm_otg_notify_charger(motg, mA);
435
436 return 0;
437}
438
412static void msm_otg_start_host(struct otg_transceiver *otg, int on) 439static void msm_otg_start_host(struct otg_transceiver *otg, int on)
413{ 440{
414 struct msm_otg *motg = container_of(otg, struct msm_otg, otg); 441 struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
@@ -563,6 +590,306 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg,
563 return 0; 590 return 0;
564} 591}
565 592
593static bool msm_chg_check_secondary_det(struct msm_otg *motg)
594{
595 struct otg_transceiver *otg = &motg->otg;
596 u32 chg_det;
597 bool ret = false;
598
599 switch (motg->pdata->phy_type) {
600 case CI_45NM_INTEGRATED_PHY:
601 chg_det = ulpi_read(otg, 0x34);
602 ret = chg_det & (1 << 4);
603 break;
604 case SNPS_28NM_INTEGRATED_PHY:
605 chg_det = ulpi_read(otg, 0x87);
606 ret = chg_det & 1;
607 break;
608 default:
609 break;
610 }
611 return ret;
612}
613
614static void msm_chg_enable_secondary_det(struct msm_otg *motg)
615{
616 struct otg_transceiver *otg = &motg->otg;
617 u32 chg_det;
618
619 switch (motg->pdata->phy_type) {
620 case CI_45NM_INTEGRATED_PHY:
621 chg_det = ulpi_read(otg, 0x34);
622 /* Turn off charger block */
623 chg_det |= ~(1 << 1);
624 ulpi_write(otg, chg_det, 0x34);
625 udelay(20);
626 /* control chg block via ULPI */
627 chg_det &= ~(1 << 3);
628 ulpi_write(otg, chg_det, 0x34);
629 /* put it in host mode for enabling D- source */
630 chg_det &= ~(1 << 2);
631 ulpi_write(otg, chg_det, 0x34);
632 /* Turn on chg detect block */
633 chg_det &= ~(1 << 1);
634 ulpi_write(otg, chg_det, 0x34);
635 udelay(20);
636 /* enable chg detection */
637 chg_det &= ~(1 << 0);
638 ulpi_write(otg, chg_det, 0x34);
639 break;
640 case SNPS_28NM_INTEGRATED_PHY:
641 /*
642 * Configure DM as current source, DP as current sink
643 * and enable battery charging comparators.
644 */
645 ulpi_write(otg, 0x8, 0x85);
646 ulpi_write(otg, 0x2, 0x85);
647 ulpi_write(otg, 0x1, 0x85);
648 break;
649 default:
650 break;
651 }
652}
653
654static bool msm_chg_check_primary_det(struct msm_otg *motg)
655{
656 struct otg_transceiver *otg = &motg->otg;
657 u32 chg_det;
658 bool ret = false;
659
660 switch (motg->pdata->phy_type) {
661 case CI_45NM_INTEGRATED_PHY:
662 chg_det = ulpi_read(otg, 0x34);
663 ret = chg_det & (1 << 4);
664 break;
665 case SNPS_28NM_INTEGRATED_PHY:
666 chg_det = ulpi_read(otg, 0x87);
667 ret = chg_det & 1;
668 break;
669 default:
670 break;
671 }
672 return ret;
673}
674
675static void msm_chg_enable_primary_det(struct msm_otg *motg)
676{
677 struct otg_transceiver *otg = &motg->otg;
678 u32 chg_det;
679
680 switch (motg->pdata->phy_type) {
681 case CI_45NM_INTEGRATED_PHY:
682 chg_det = ulpi_read(otg, 0x34);
683 /* enable chg detection */
684 chg_det &= ~(1 << 0);
685 ulpi_write(otg, chg_det, 0x34);
686 break;
687 case SNPS_28NM_INTEGRATED_PHY:
688 /*
689 * Configure DP as current source, DM as current sink
690 * and enable battery charging comparators.
691 */
692 ulpi_write(otg, 0x2, 0x85);
693 ulpi_write(otg, 0x1, 0x85);
694 break;
695 default:
696 break;
697 }
698}
699
700static bool msm_chg_check_dcd(struct msm_otg *motg)
701{
702 struct otg_transceiver *otg = &motg->otg;
703 u32 line_state;
704 bool ret = false;
705
706 switch (motg->pdata->phy_type) {
707 case CI_45NM_INTEGRATED_PHY:
708 line_state = ulpi_read(otg, 0x15);
709 ret = !(line_state & 1);
710 break;
711 case SNPS_28NM_INTEGRATED_PHY:
712 line_state = ulpi_read(otg, 0x87);
713 ret = line_state & 2;
714 break;
715 default:
716 break;
717 }
718 return ret;
719}
720
721static void msm_chg_disable_dcd(struct msm_otg *motg)
722{
723 struct otg_transceiver *otg = &motg->otg;
724 u32 chg_det;
725
726 switch (motg->pdata->phy_type) {
727 case CI_45NM_INTEGRATED_PHY:
728 chg_det = ulpi_read(otg, 0x34);
729 chg_det &= ~(1 << 5);
730 ulpi_write(otg, chg_det, 0x34);
731 break;
732 case SNPS_28NM_INTEGRATED_PHY:
733 ulpi_write(otg, 0x10, 0x86);
734 break;
735 default:
736 break;
737 }
738}
739
740static void msm_chg_enable_dcd(struct msm_otg *motg)
741{
742 struct otg_transceiver *otg = &motg->otg;
743 u32 chg_det;
744
745 switch (motg->pdata->phy_type) {
746 case CI_45NM_INTEGRATED_PHY:
747 chg_det = ulpi_read(otg, 0x34);
748 /* Turn on D+ current source */
749 chg_det |= (1 << 5);
750 ulpi_write(otg, chg_det, 0x34);
751 break;
752 case SNPS_28NM_INTEGRATED_PHY:
753 /* Data contact detection enable */
754 ulpi_write(otg, 0x10, 0x85);
755 break;
756 default:
757 break;
758 }
759}
760
761static void msm_chg_block_on(struct msm_otg *motg)
762{
763 struct otg_transceiver *otg = &motg->otg;
764 u32 func_ctrl, chg_det;
765
766 /* put the controller in non-driving mode */
767 func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL);
768 func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
769 func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
770 ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL);
771
772 switch (motg->pdata->phy_type) {
773 case CI_45NM_INTEGRATED_PHY:
774 chg_det = ulpi_read(otg, 0x34);
775 /* control chg block via ULPI */
776 chg_det &= ~(1 << 3);
777 ulpi_write(otg, chg_det, 0x34);
778 /* Turn on chg detect block */
779 chg_det &= ~(1 << 1);
780 ulpi_write(otg, chg_det, 0x34);
781 udelay(20);
782 break;
783 case SNPS_28NM_INTEGRATED_PHY:
784 /* Clear charger detecting control bits */
785 ulpi_write(otg, 0x3F, 0x86);
786 /* Clear alt interrupt latch and enable bits */
787 ulpi_write(otg, 0x1F, 0x92);
788 ulpi_write(otg, 0x1F, 0x95);
789 udelay(100);
790 break;
791 default:
792 break;
793 }
794}
795
796static void msm_chg_block_off(struct msm_otg *motg)
797{
798 struct otg_transceiver *otg = &motg->otg;
799 u32 func_ctrl, chg_det;
800
801 switch (motg->pdata->phy_type) {
802 case CI_45NM_INTEGRATED_PHY:
803 chg_det = ulpi_read(otg, 0x34);
804 /* Turn off charger block */
805 chg_det |= ~(1 << 1);
806 ulpi_write(otg, chg_det, 0x34);
807 break;
808 case SNPS_28NM_INTEGRATED_PHY:
809 /* Clear charger detecting control bits */
810 ulpi_write(otg, 0x3F, 0x86);
811 /* Clear alt interrupt latch and enable bits */
812 ulpi_write(otg, 0x1F, 0x92);
813 ulpi_write(otg, 0x1F, 0x95);
814 break;
815 default:
816 break;
817 }
818
819 /* put the controller in normal mode */
820 func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL);
821 func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
822 func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
823 ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL);
824}
825
826#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */
827#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */
828#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */
829#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */
830static void msm_chg_detect_work(struct work_struct *w)
831{
832 struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
833 struct otg_transceiver *otg = &motg->otg;
834 bool is_dcd, tmout, vout;
835 unsigned long delay;
836
837 dev_dbg(otg->dev, "chg detection work\n");
838 switch (motg->chg_state) {
839 case USB_CHG_STATE_UNDEFINED:
840 pm_runtime_get_sync(otg->dev);
841 msm_chg_block_on(motg);
842 msm_chg_enable_dcd(motg);
843 motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
844 motg->dcd_retries = 0;
845 delay = MSM_CHG_DCD_POLL_TIME;
846 break;
847 case USB_CHG_STATE_WAIT_FOR_DCD:
848 is_dcd = msm_chg_check_dcd(motg);
849 tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
850 if (is_dcd || tmout) {
851 msm_chg_disable_dcd(motg);
852 msm_chg_enable_primary_det(motg);
853 delay = MSM_CHG_PRIMARY_DET_TIME;
854 motg->chg_state = USB_CHG_STATE_DCD_DONE;
855 } else {
856 delay = MSM_CHG_DCD_POLL_TIME;
857 }
858 break;
859 case USB_CHG_STATE_DCD_DONE:
860 vout = msm_chg_check_primary_det(motg);
861 if (vout) {
862 msm_chg_enable_secondary_det(motg);
863 delay = MSM_CHG_SECONDARY_DET_TIME;
864 motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
865 } else {
866 motg->chg_type = USB_SDP_CHARGER;
867 motg->chg_state = USB_CHG_STATE_DETECTED;
868 delay = 0;
869 }
870 break;
871 case USB_CHG_STATE_PRIMARY_DONE:
872 vout = msm_chg_check_secondary_det(motg);
873 if (vout)
874 motg->chg_type = USB_DCP_CHARGER;
875 else
876 motg->chg_type = USB_CDP_CHARGER;
877 motg->chg_state = USB_CHG_STATE_SECONDARY_DONE;
878 /* fall through */
879 case USB_CHG_STATE_SECONDARY_DONE:
880 motg->chg_state = USB_CHG_STATE_DETECTED;
881 case USB_CHG_STATE_DETECTED:
882 msm_chg_block_off(motg);
883 dev_dbg(otg->dev, "charger = %d\n", motg->chg_type);
884 schedule_work(&motg->sm_work);
885 return;
886 default:
887 return;
888 }
889
890 schedule_delayed_work(&motg->chg_work, delay);
891}
892
566/* 893/*
567 * We support OTG, Peripheral only and Host only configurations. In case 894 * We support OTG, Peripheral only and Host only configurations. In case
568 * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen 895 * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen
@@ -633,9 +960,48 @@ static void msm_otg_sm_work(struct work_struct *w)
633 writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); 960 writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
634 msm_otg_start_host(otg, 1); 961 msm_otg_start_host(otg, 1);
635 otg->state = OTG_STATE_A_HOST; 962 otg->state = OTG_STATE_A_HOST;
636 } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { 963 } else if (test_bit(B_SESS_VLD, &motg->inputs)) {
637 msm_otg_start_peripheral(otg, 1); 964 switch (motg->chg_state) {
638 otg->state = OTG_STATE_B_PERIPHERAL; 965 case USB_CHG_STATE_UNDEFINED:
966 msm_chg_detect_work(&motg->chg_work.work);
967 break;
968 case USB_CHG_STATE_DETECTED:
969 switch (motg->chg_type) {
970 case USB_DCP_CHARGER:
971 msm_otg_notify_charger(motg,
972 IDEV_CHG_MAX);
973 break;
974 case USB_CDP_CHARGER:
975 msm_otg_notify_charger(motg,
976 IDEV_CHG_MAX);
977 msm_otg_start_peripheral(otg, 1);
978 otg->state = OTG_STATE_B_PERIPHERAL;
979 break;
980 case USB_SDP_CHARGER:
981 msm_otg_notify_charger(motg, IUNIT);
982 msm_otg_start_peripheral(otg, 1);
983 otg->state = OTG_STATE_B_PERIPHERAL;
984 break;
985 default:
986 break;
987 }
988 break;
989 default:
990 break;
991 }
992 } else {
993 /*
994 * If charger detection work is pending, decrement
995 * the pm usage counter to balance with the one that
996 * is incremented in charger detection work.
997 */
998 if (cancel_delayed_work_sync(&motg->chg_work)) {
999 pm_runtime_put_sync(otg->dev);
1000 msm_otg_reset(otg);
1001 }
1002 msm_otg_notify_charger(motg, 0);
1003 motg->chg_state = USB_CHG_STATE_UNDEFINED;
1004 motg->chg_type = USB_INVALID_CHARGER;
639 } 1005 }
640 pm_runtime_put_sync(otg->dev); 1006 pm_runtime_put_sync(otg->dev);
641 break; 1007 break;
@@ -643,7 +1009,10 @@ static void msm_otg_sm_work(struct work_struct *w)
643 dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); 1009 dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n");
644 if (!test_bit(B_SESS_VLD, &motg->inputs) || 1010 if (!test_bit(B_SESS_VLD, &motg->inputs) ||
645 !test_bit(ID, &motg->inputs)) { 1011 !test_bit(ID, &motg->inputs)) {
1012 msm_otg_notify_charger(motg, 0);
646 msm_otg_start_peripheral(otg, 0); 1013 msm_otg_start_peripheral(otg, 0);
1014 motg->chg_state = USB_CHG_STATE_UNDEFINED;
1015 motg->chg_type = USB_INVALID_CHARGER;
647 otg->state = OTG_STATE_B_IDLE; 1016 otg->state = OTG_STATE_B_IDLE;
648 msm_otg_reset(otg); 1017 msm_otg_reset(otg);
649 schedule_work(w); 1018 schedule_work(w);
@@ -935,6 +1304,7 @@ static int __init msm_otg_probe(struct platform_device *pdev)
935 writel(0, USB_OTGSC); 1304 writel(0, USB_OTGSC);
936 1305
937 INIT_WORK(&motg->sm_work, msm_otg_sm_work); 1306 INIT_WORK(&motg->sm_work, msm_otg_sm_work);
1307 INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
938 ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, 1308 ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
939 "msm_otg", motg); 1309 "msm_otg", motg);
940 if (ret) { 1310 if (ret) {
@@ -945,6 +1315,7 @@ static int __init msm_otg_probe(struct platform_device *pdev)
945 otg->init = msm_otg_reset; 1315 otg->init = msm_otg_reset;
946 otg->set_host = msm_otg_set_host; 1316 otg->set_host = msm_otg_set_host;
947 otg->set_peripheral = msm_otg_set_peripheral; 1317 otg->set_peripheral = msm_otg_set_peripheral;
1318 otg->set_power = msm_otg_set_power;
948 1319
949 otg->io_ops = &msm_otg_io_ops; 1320 otg->io_ops = &msm_otg_io_ops;
950 1321
@@ -1004,6 +1375,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
1004 return -EBUSY; 1375 return -EBUSY;
1005 1376
1006 msm_otg_debugfs_cleanup(); 1377 msm_otg_debugfs_cleanup();
1378 cancel_delayed_work_sync(&motg->chg_work);
1007 cancel_work_sync(&motg->sm_work); 1379 cancel_work_sync(&motg->sm_work);
1008 1380
1009 pm_runtime_resume(&pdev->dev); 1381 pm_runtime_resume(&pdev->dev);