88#include <linux/err.h>
99#include <linux/io.h>
1010#include <linux/delay.h>
11+ #include <linux/usb/otg.h>
1112
1213#include "ci_hdrc_imx.h"
1314
99100#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1)
100101#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
101102#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
103+ /* The default DM/DP value is pull-down */
104+ #define MX7D_USBNC_USB_CTRL2_OPMODE (v ) (v << 6)
105+ #define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1)
106+ #define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
107+ #define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
108+ #define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
109+ #define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
110+ #define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
111+ #define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
112+ #define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
113+ BIT(14) | BIT(15))
114+
115+ #define MX7D_USB_OTG_PHY_CFG1 0x30
116+ #define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
117+ #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
118+ #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
119+ #define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
120+ #define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16)
121+
122+ #define MX7D_USB_OTG_PHY_CFG2 0x34
123+
124+ #define MX7D_USB_OTG_PHY_STATUS 0x3c
125+ #define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
126+ #define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
127+ #define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
128+ #define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
102129
103130#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
104131 MX6_BM_ID_WAKEUP)
@@ -114,6 +141,8 @@ struct usbmisc_ops {
114141 int (* hsic_set_connect )(struct imx_usbmisc_data * data );
115142 /* It's called during suspend/resume */
116143 int (* hsic_set_clk )(struct imx_usbmisc_data * data , bool enabled );
144+ /* usb charger detection */
145+ int (* charger_detection )(struct imx_usbmisc_data * data );
117146};
118147
119148struct imx_usbmisc {
@@ -621,6 +650,188 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
621650 return 0 ;
622651}
623652
653+ static int imx7d_charger_secondary_detection (struct imx_usbmisc_data * data )
654+ {
655+ struct imx_usbmisc * usbmisc = dev_get_drvdata (data -> dev );
656+ struct usb_phy * usb_phy = data -> usb_phy ;
657+ int val ;
658+ unsigned long flags ;
659+
660+ /* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
661+ spin_lock_irqsave (& usbmisc -> lock , flags );
662+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
663+ writel (val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
664+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
665+ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL ,
666+ usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
667+ spin_unlock_irqrestore (& usbmisc -> lock , flags );
668+
669+ usleep_range (1000 , 2000 );
670+
671+ /*
672+ * Per BC 1.2, check voltage of D+:
673+ * DCP: if greater than VDAT_REF;
674+ * CDP: if less than VDAT_REF.
675+ */
676+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_STATUS );
677+ if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET ) {
678+ dev_dbg (data -> dev , "It is a dedicate charging port\n" );
679+ usb_phy -> chg_type = DCP_TYPE ;
680+ } else {
681+ dev_dbg (data -> dev , "It is a charging downstream port\n" );
682+ usb_phy -> chg_type = CDP_TYPE ;
683+ }
684+
685+ return 0 ;
686+ }
687+
688+ static void imx7_disable_charger_detector (struct imx_usbmisc_data * data )
689+ {
690+ struct imx_usbmisc * usbmisc = dev_get_drvdata (data -> dev );
691+ unsigned long flags ;
692+ u32 val ;
693+
694+ spin_lock_irqsave (& usbmisc -> lock , flags );
695+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
696+ val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
697+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
698+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
699+ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL );
700+ writel (val , usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
701+
702+ /* Set OPMODE to be 2'b00 and disable its override */
703+ val = readl (usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
704+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK ;
705+ writel (val , usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
706+
707+ val = readl (usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
708+ writel (val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN ,
709+ usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
710+ spin_unlock_irqrestore (& usbmisc -> lock , flags );
711+ }
712+
713+ static int imx7d_charger_data_contact_detect (struct imx_usbmisc_data * data )
714+ {
715+ struct imx_usbmisc * usbmisc = dev_get_drvdata (data -> dev );
716+ unsigned long flags ;
717+ u32 val ;
718+ int i , data_pin_contact_count = 0 ;
719+
720+ /* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
721+ spin_lock_irqsave (& usbmisc -> lock , flags );
722+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
723+ writel (val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB ,
724+ usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
725+ spin_unlock_irqrestore (& usbmisc -> lock , flags );
726+
727+ for (i = 0 ; i < 100 ; i = i + 1 ) {
728+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_STATUS );
729+ if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 )) {
730+ if (data_pin_contact_count ++ > 5 )
731+ /* Data pin makes contact */
732+ break ;
733+ usleep_range (5000 , 10000 );
734+ } else {
735+ data_pin_contact_count = 0 ;
736+ usleep_range (5000 , 6000 );
737+ }
738+ }
739+
740+ /* Disable DCD after finished data contact check */
741+ spin_lock_irqsave (& usbmisc -> lock , flags );
742+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
743+ writel (val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB ,
744+ usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
745+ spin_unlock_irqrestore (& usbmisc -> lock , flags );
746+
747+ if (i == 100 ) {
748+ dev_err (data -> dev ,
749+ "VBUS is coming from a dedicated power supply.\n" );
750+ return - ENXIO ;
751+ }
752+
753+ return 0 ;
754+ }
755+
756+ static int imx7d_charger_primary_detection (struct imx_usbmisc_data * data )
757+ {
758+ struct imx_usbmisc * usbmisc = dev_get_drvdata (data -> dev );
759+ struct usb_phy * usb_phy = data -> usb_phy ;
760+ unsigned long flags ;
761+ u32 val ;
762+
763+ /* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
764+ spin_lock_irqsave (& usbmisc -> lock , flags );
765+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
766+ val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL ;
767+ writel (val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
768+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 ,
769+ usbmisc -> base + MX7D_USB_OTG_PHY_CFG2 );
770+ spin_unlock_irqrestore (& usbmisc -> lock , flags );
771+
772+ usleep_range (1000 , 2000 );
773+
774+ /* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
775+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_STATUS );
776+ if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET )) {
777+ dev_dbg (data -> dev , "It is a standard downstream port\n" );
778+ usb_phy -> chg_type = SDP_TYPE ;
779+ }
780+
781+ return 0 ;
782+ }
783+
784+ /**
785+ * Whole charger detection process:
786+ * 1. OPMODE override to be non-driving
787+ * 2. Data contact check
788+ * 3. Primary detection
789+ * 4. Secondary detection
790+ * 5. Disable charger detection
791+ */
792+ static int imx7d_charger_detection (struct imx_usbmisc_data * data )
793+ {
794+ struct imx_usbmisc * usbmisc = dev_get_drvdata (data -> dev );
795+ struct usb_phy * usb_phy = data -> usb_phy ;
796+ unsigned long flags ;
797+ u32 val ;
798+ int ret ;
799+
800+ /* Check if vbus is valid */
801+ val = readl (usbmisc -> base + MX7D_USB_OTG_PHY_STATUS );
802+ if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD )) {
803+ dev_err (data -> dev , "vbus is error\n" );
804+ return - EINVAL ;
805+ }
806+
807+ /*
808+ * Keep OPMODE to be non-driving mode during the whole
809+ * charger detection process.
810+ */
811+ spin_lock_irqsave (& usbmisc -> lock , flags );
812+ val = readl (usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
813+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK ;
814+ val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING ;
815+ writel (val , usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
816+
817+ val = readl (usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
818+ writel (val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN ,
819+ usbmisc -> base + MX7D_USBNC_USB_CTRL2 );
820+ spin_unlock_irqrestore (& usbmisc -> lock , flags );
821+
822+ ret = imx7d_charger_data_contact_detect (data );
823+ if (ret )
824+ return ret ;
825+
826+ ret = imx7d_charger_primary_detection (data );
827+ if (!ret && usb_phy -> chg_type != SDP_TYPE )
828+ ret = imx7d_charger_secondary_detection (data );
829+
830+ imx7_disable_charger_detector (data );
831+
832+ return ret ;
833+ }
834+
624835static const struct usbmisc_ops imx25_usbmisc_ops = {
625836 .init = usbmisc_imx25_init ,
626837 .post = usbmisc_imx25_post ,
@@ -659,6 +870,7 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = {
659870static const struct usbmisc_ops imx7d_usbmisc_ops = {
660871 .init = usbmisc_imx7d_init ,
661872 .set_wakeup = usbmisc_imx7d_set_wakeup ,
873+ .charger_detection = imx7d_charger_detection ,
662874};
663875
664876static inline bool is_imx53_usbmisc (struct imx_usbmisc_data * data )
@@ -737,6 +949,39 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
737949 return usbmisc -> ops -> hsic_set_clk (data , on );
738950}
739951EXPORT_SYMBOL_GPL (imx_usbmisc_hsic_set_clk );
952+
953+ int imx_usbmisc_charger_detection (struct imx_usbmisc_data * data , bool connect )
954+ {
955+ struct imx_usbmisc * usbmisc ;
956+ struct usb_phy * usb_phy ;
957+ int ret = 0 ;
958+
959+ if (!data )
960+ return - EINVAL ;
961+
962+ usbmisc = dev_get_drvdata (data -> dev );
963+ usb_phy = data -> usb_phy ;
964+ if (!usbmisc -> ops -> charger_detection )
965+ return - ENOTSUPP ;
966+
967+ if (connect ) {
968+ ret = usbmisc -> ops -> charger_detection (data );
969+ if (ret ) {
970+ dev_err (data -> dev ,
971+ "Error occurs during detection: %d\n" ,
972+ ret );
973+ usb_phy -> chg_state = USB_CHARGER_ABSENT ;
974+ } else {
975+ usb_phy -> chg_state = USB_CHARGER_PRESENT ;
976+ }
977+ } else {
978+ usb_phy -> chg_state = USB_CHARGER_ABSENT ;
979+ usb_phy -> chg_type = UNKNOWN_TYPE ;
980+ }
981+ return ret ;
982+ }
983+ EXPORT_SYMBOL_GPL (imx_usbmisc_charger_detection );
984+
740985static const struct of_device_id usbmisc_imx_dt_ids [] = {
741986 {
742987 .compatible = "fsl,imx25-usbmisc" ,
0 commit comments