USB: OTG: msm: Add support for proprietary charger detection
Proprietary chargers pull D+/- to specific voltages between 2.0-3.3V
(VLGC) for identification. The DM voltage may not exceed VLGC due
to pull-down resistor. Hence a proprietary charger is detected as
a Standard downstream port (SDP). Read line state to distinguish
between SDP and proprietary charger. The DP voltage would exceed
VLGC for proprietary chargers.
D- can be greater than VLGC when a low speed device is attached via
Accessory charger adapter (ACA-A). Hence mark the charger as ACA-A
if ID resistance is RID_A and D- > VLGC
CRs-Fixed: 368688
Change-Id: I3497efbe025abf150078a19d69254f75f5df5b2a
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 14118e7..771dbb7 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1001,7 +1001,8 @@
charger_type = POWER_SUPPLY_TYPE_USB;
else if (motg->chg_type == USB_CDP_CHARGER)
charger_type = POWER_SUPPLY_TYPE_USB_CDP;
- else if (motg->chg_type == USB_DCP_CHARGER)
+ else if (motg->chg_type == USB_DCP_CHARGER ||
+ motg->chg_type == USB_PROPRIETARY_CHARGER)
charger_type = POWER_SUPPLY_TYPE_USB_DCP;
else if ((motg->chg_type == USB_ACA_DOCK_CHARGER ||
motg->chg_type == USB_ACA_A_CHARGER ||
@@ -1627,9 +1628,6 @@
ulpi_write(phy, chg_det, 0x34);
break;
case SNPS_28NM_INTEGRATED_PHY:
- /* Turn off VDP_SRC */
- ulpi_write(phy, 0x3, 0x86);
- msleep(20);
/*
* Configure DM as current source, DP as current sink
* and enable battery charging comparators.
@@ -1657,6 +1655,9 @@
case SNPS_28NM_INTEGRATED_PHY:
chg_det = ulpi_read(phy, 0x87);
ret = chg_det & 1;
+ /* Turn off VDP_SRC */
+ ulpi_write(phy, 0x3, 0x86);
+ msleep(20);
break;
default:
break;
@@ -1825,6 +1826,7 @@
case USB_ACA_B_CHARGER: return "USB_ACA_B_CHARGER";
case USB_ACA_C_CHARGER: return "USB_ACA_C_CHARGER";
case USB_ACA_DOCK_CHARGER: return "USB_ACA_DOCK_CHARGER";
+ case USB_PROPRIETARY_CHARGER: return "USB_PROPRIETARY_CHARGER";
default: return "INVALID_CHARGER";
}
}
@@ -1838,6 +1840,7 @@
struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
struct usb_phy *phy = &motg->phy;
bool is_dcd = false, tmout, vout, is_aca;
+ u32 line_state, dm_vlgc;
unsigned long delay;
dev_dbg(phy->dev, "chg detection work\n");
@@ -1880,24 +1883,37 @@
break;
case USB_CHG_STATE_DCD_DONE:
vout = msm_chg_check_primary_det(motg);
- if (vout) {
+ line_state = readl_relaxed(USB_PORTSC) & PORTSC_LS;
+ dm_vlgc = line_state & PORTSC_LS_DM;
+ if (vout && !dm_vlgc) { /* VDAT_REF < DM < VLGC */
if (test_bit(ID_A, &motg->inputs)) {
motg->chg_type = USB_ACA_DOCK_CHARGER;
motg->chg_state = USB_CHG_STATE_DETECTED;
delay = 0;
break;
}
- msm_chg_enable_secondary_det(motg);
- delay = MSM_CHG_SECONDARY_DET_TIME;
- motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
- } else {
+ if (line_state) { /* DP > VLGC */
+ motg->chg_type = USB_PROPRIETARY_CHARGER;
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ delay = 0;
+ } else {
+ msm_chg_enable_secondary_det(motg);
+ delay = MSM_CHG_SECONDARY_DET_TIME;
+ motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+ }
+ } else { /* DM < VDAT_REF || DM > VLGC */
if (test_bit(ID_A, &motg->inputs)) {
motg->chg_type = USB_ACA_A_CHARGER;
motg->chg_state = USB_CHG_STATE_DETECTED;
delay = 0;
break;
}
- motg->chg_type = USB_SDP_CHARGER;
+
+ if (line_state) /* DP > VLGC or/and DM > VLGC */
+ motg->chg_type = USB_PROPRIETARY_CHARGER;
+ else
+ motg->chg_type = USB_SDP_CHARGER;
+
motg->chg_state = USB_CHG_STATE_DETECTED;
delay = 0;
}
@@ -2050,6 +2066,8 @@
case USB_DCP_CHARGER:
/* Enable VDP_SRC */
ulpi_write(otg->phy, 0x2, 0x85);
+ /* fall through */
+ case USB_PROPRIETARY_CHARGER:
msm_otg_notify_charger(motg,
IDEV_CHG_MAX);
pm_runtime_put_noidle(otg->phy->dev);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 9da1999..68c1ffc 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -137,6 +137,9 @@
* or more downstream ports. Capable of supplying
* IDEV_CHG_MAX irrespective of devices connected on
* accessory ports.
+ * USB_PROPRIETARY_CHARGER A proprietary charger pull DP and DM to specific
+ * voltages between 2.0-3.3v for identification.
+ *
*/
enum usb_chg_type {
USB_INVALID_CHARGER = 0,
@@ -147,6 +150,7 @@
USB_ACA_B_CHARGER,
USB_ACA_C_CHARGER,
USB_ACA_DOCK_CHARGER,
+ USB_PROPRIETARY_CHARGER,
};
/**
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 8a05136..516f764 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -33,6 +33,8 @@
#define PORTSC_PHCD (1 << 23) /* phy suspend mode */
#define PORTSC_PTS_MASK (3 << 30)
#define PORTSC_PTS_ULPI (3 << 30)
+#define PORTSC_LS (3 << 10)
+#define PORTSC_LS_DM (1 << 10)
#define PORTSC_CSC (1 << 1)
#define PORTSC_CCS (1 << 0)