dev_dbg(pnw->dev, "%s <---\n", __func__);
};
+static int penwell_otg_charger_det_dcd_clt(void)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ struct intel_mid_otg_xceiv *iotg = &pnw->iotg;
+ int retval;
+ unsigned long timeout, interval;
+ u8 data;
+
+ /* Change OPMODE to '01' Non-driving */
+ retval = penwell_otg_ulpi_write(iotg, ULPI_FUNCTRLSET,
+ OPMODE0 | XCVRSELECT0);
+ if (retval)
+ return retval;
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_FUNCTRLCLR,
+ OPMODE1 | XCVRSELECT1 | TERMSELECT);
+ if (retval)
+ return retval;
+
+ /* Disable DP pulldown to allow weak Idp_src for DCD */
+ retval = penwell_otg_ulpi_write(iotg, ULPI_OTGCTRLCLR, DPPULLDOWN);
+ if (retval)
+ return retval;
+
+ /* Enable SW control */
+ retval = penwell_otg_ulpi_write(iotg, ULPI_PWRCTRLSET, SWCNTRL);
+ if (retval)
+ return retval;
+
+ /* Enable IDPSRC */
+ retval = penwell_otg_ulpi_write(iotg, ULPI_VS3SET, CHGD_IDP_SRC);
+ if (retval)
+ return retval;
+
+ /* Check DCD result, use same polling parameter */
+ timeout = jiffies + msecs_to_jiffies(DATACON_TIMEOUT);
+ interval = DATACON_INTERVAL * 1000; /* us */
+
+ while (!time_after(jiffies, timeout)) {
+ retval = penwell_otg_ulpi_read(iotg, ULPI_VS4, &data);
+ if (retval) {
+ dev_warn(pnw->dev, "Failed to read ULPI register\n");
+ return retval;
+ }
+
+ if (data & !CHRG_SERX_DP) {
+ dev_info(pnw->dev, "Data contact detected!\n");
+ break;
+ }
+
+ /* Polling interval */
+ usleep_range(interval, interval + 2000);
+ }
+
+ /* ulpi_write(0x87, 0x40)*/
+ retval = penwell_otg_ulpi_write(iotg, ULPI_VS3CLR, CHGD_IDP_SRC);
+ if (retval)
+ return retval;
+
+ dev_info(pnw->dev, "DCD complete\n");
+
+ return 0;
+}
+
+static int penwell_otg_charger_det_se1_clt(void)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ struct intel_mid_otg_xceiv *iotg = &pnw->iotg;
+ int retval;
+ u8 data;
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_OTGCTRLSET,
+ DMPULLDOWN | DPPULLDOWN);
+ if (retval)
+ return retval;
+
+ retval = penwell_otg_ulpi_read(iotg, ULPI_VS4, &data);
+ if (retval) {
+ dev_warn(pnw->dev, "ULPI read failed, exit\n");
+ return -EBUSY;
+ }
+
+ if ((data & CHRG_SERX_DP) && (data & CHRG_SERX_DM))
+ return CHRG_SE1;
+
+ return 0;
+}
+
+static int penwell_otg_charger_det_pri_clt(void)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ struct intel_mid_otg_xceiv *iotg = &pnw->iotg;
+ int retval;
+ u8 vdat_det, serx_dm;
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_PWRCTRLSET, DPVSRCEN);
+ if (retval)
+ return retval;
+
+ msleep(110);
+
+ retval = penwell_otg_ulpi_read(iotg, ULPI_PWRCTRL, &vdat_det);
+ if (retval) {
+ dev_warn(pnw->dev, "ULPI read failed, exit\n");
+ return -EBUSY;
+ }
+
+ retval = penwell_otg_ulpi_read(iotg, ULPI_VS4, &serx_dm);
+ if (retval) {
+ dev_warn(pnw->dev, "ULPI read failed, exit\n");
+ return -EBUSY;
+ }
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_PWRCTRLCLR, DPVSRCEN);
+ if (retval)
+ return retval;
+
+ vdat_det &= VDATDET;
+ serx_dm &= CHRG_SERX_DM;
+
+ if ((!vdat_det) || serx_dm)
+ return CHRG_SDP;
+
+ return 0;
+}
+
+static int penwell_otg_charger_det_sec_clt(void)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ struct intel_mid_otg_xceiv *iotg = &pnw->iotg;
+ int retval;
+ u8 vdat_det;
+
+ usleep_range(1000, 1500);
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_VS1CLR, DATAPOLARITY);
+ if (retval)
+ return retval;
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_PWRCTRLSET, DPVSRCEN);
+ if (retval)
+ return retval;
+
+ msleep(80);
+
+ retval = penwell_otg_ulpi_read(iotg, ULPI_PWRCTRL, &vdat_det);
+ if (retval) {
+ dev_warn(pnw->dev, "ULPI read failed, exit\n");
+ return retval;
+ }
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_PWRCTRLCLR, DPVSRCEN);
+ if (retval)
+ return retval;
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_VS1SET, DATAPOLARITY);
+ if (retval)
+ return retval;
+
+ vdat_det &= VDATDET;
+
+ if (vdat_det)
+ return CHRG_DCP;
+ else
+ return CHRG_CDP;
+
+ return 0;
+}
+
+static int penwell_otg_charger_det_clean_clt(void)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ struct intel_mid_otg_xceiv *iotg = &pnw->iotg;
+ int retval;
+
+ retval = penwell_otg_ulpi_write(iotg, ULPI_PWRCTRLSET,
+ DPVSRCEN | SWCNTRL);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+static int penwell_otg_charger_det_clt(void)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ struct intel_mid_otg_xceiv *iotg = &pnw->iotg;
+ int retval;
+ u8 data;
+
+ dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+ /* DCD */
+ retval = penwell_otg_charger_det_dcd_clt();
+ if (retval) {
+ dev_warn(pnw->dev, "DCD failed, exit\n");
+ return CHRG_UNKNOWN;
+ }
+
+ /* SE1 Detection */
+ retval = penwell_otg_charger_det_se1_clt();
+ if (retval < 0) {
+ dev_warn(pnw->dev, "SE1 Det failed, exit\n");
+ return CHRG_UNKNOWN;
+ } else if (retval == CHRG_SE1) {
+ dev_info(pnw->dev, "SE1 detected\n");
+ return CHRG_SE1;
+ }
+
+ /* Pri Det */
+ retval = penwell_otg_charger_det_pri_clt();
+ if (retval < 0) {
+ dev_warn(pnw->dev, "Pri Det failed, exit\n");
+ return CHRG_UNKNOWN;
+ } else if (retval == CHRG_SDP) {
+ dev_info(pnw->dev, "SDP detected\n");
+ return CHRG_SDP;
+ }
+
+ /* Sec Det */
+ retval = penwell_otg_charger_det_sec_clt();
+ if (retval < 0) {
+ dev_warn(pnw->dev, "Sec Det failed, exit\n");
+ return CHRG_UNKNOWN;
+ } else if (retval == CHRG_CDP) {
+ dev_info(pnw->dev, "CDP detected\n");
+ return CHRG_CDP;
+ } else if (retval == CHRG_DCP) {
+ dev_info(pnw->dev, "DCP detected\n");
+ penwell_otg_charger_det_clean_clt();
+ return CHRG_DCP;
+ }
+
+ dev_dbg(pnw->dev, "%s <---\n", __func__);
+
+ return 0;
+}
+
void penwell_otg_phy_vbus_wakeup(bool on)
{
struct penwell_otg *pnw = the_transceiver;
charger_type = retval;
}
} else {
- /*
- * Force CHRG_SDP for Clovertrail,
- * need fix later.
- */
- charger_type = CHRG_SDP;
+ /* Clovertrail charger detection flow */
+ retval = penwell_otg_charger_det_clt();
+ if (retval < 0) {
+ dev_warn(pnw->dev, "Charger detect failure\n");
+ break;
+ } else
+ charger_type = retval;
}
if (charger_type == CHRG_DCP) {
#define ULPI_VS1SET 0x81
#define ULPI_VS1CLR 0x82
# define DATAPOLARITY BIT(6)
+#define ULPI_VS2STS 0x83
+#define ULPI_VS2LATCH 0x84
+# define VBUS_MNTR_STS BIT(7)
+# define REG3V3_MNTR_STS BIT(6)
+# define SVLDCONWKB_WDOG_STS BIT(5)
+# define IDFLOAT_STS BIT(4)
+# define IDRARBRC_STS1 BIT(3)
+# define IDRARBRC_STS2 BIT(2)
+# define IDRARBRC_MSK (BIT(2) | BIT(3))
+# define IDRARBRC_A BIT(2)
+# define IDRARBRC_B BIT(3)
+# define IDRARBRC_C (BIT(2) | BIT(3))
+# define BVALID_STS BIT(0)
#define MSIC_VS3 0x3b9
#define MSIC_VS3SET 0x346 /* Vendor Specific */
#define MSIC_VS3CLR 0x347
# define R1KERIES BIT(4)
# define CHRG_SERX_DP BIT(1)
# define CHRG_SERX_DM BIT(0)
+#define ULPI_VS5 0x8b
+#define ULPI_VS5SET 0x8c
+#define ULPI_VS5CLR 0x8d
+# define AUTORESUME_WDOG BIT(6)
+# define IDFLOAT_EN BIT(5)
+# define IDRES_EN BIT(4)
+# define SVLDCONWKB_WDOG BIT(3)
+# define VBUS_MNTR_RISEEN BIT(2)
+# define VBUS_MNTR_FALLEN BIT(1)
+# define REG3V3IN_MNTR_EN BIT(0)
+#define ULPI_VS6 0x8e
+#define ULPI_VS6SET 0x8f
+#define ULPI_VS6CLR 0x90
+# define ACA_RID_B_CFG BIT(7)
+# define ACA_RID_A_CFG BIT(6)
+# define SOF_EN BIT(5)
#define MSIC_ULPIACCESSMODE 0x348
# define SPIMODE BIT(0)
#define MSIC_INT_EN_RISE 0x39D
MSIC_VD_UNKNOWN
};
-/* charger defined in BC 1.1 */
+/* charger defined in BC 1.2 */
enum usb_charger_type {
CHRG_UNKNOWN,
CHRG_SDP, /* Standard Downstream Port */
CHRG_CDP, /* Charging Downstream Port */
CHRG_DCP, /* Dedicated Charging Port */
- CHRG_ACA /* Accessory Charger Adapter */
+ CHRG_ACA, /* Accessory Charger Adapter */
+ CHRG_ACA_DOCK, /* Accessory Charger Adapter - Dock */
+ CHRG_ACA_A, /* Accessory Charger Adapter - RID_A */
+ CHRG_ACA_B, /* Accessory Charger Adapter - RID_B */
+ CHRG_ACA_C, /* Accessory Charger Adapter - RID_C */
+ CHRG_SE1, /* SE1 (Apple)*/
+ CHRG_MHL /* Moblie High-Definition Link */
};
struct adp_status {