From af8986ad36b2007a2dda92f5ff4453e30d5c7862 Mon Sep 17 00:00:00 2001 From: "Wu, Hao" Date: Fri, 3 Feb 2012 20:22:25 +0800 Subject: [PATCH] usb/penwell_otg: add charger detection flow for Clovertrail BZ: 14625 This patch adds charger detection flow for Clovertrail, which is different than what we used for MFLD. Change-Id: I7d0442967babf08aa31f42209f1c79b11ce8c6d7 Signed-off-by: Wu, Hao Reviewed-on: http://android.intel.com:8080/33883 Reviewed-by: Wang, Feng A Reviewed-by: Tang, Richard Reviewed-by: Meng, Zhe Tested-by: Meng, Zhe Reviewed-by: buildbot Tested-by: buildbot --- drivers/usb/otg/penwell_otg.c | 250 +++++++++++++++++++++++++++++++++++++++- include/linux/usb/penwell_otg.h | 39 ++++++- 2 files changed, 282 insertions(+), 7 deletions(-) diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c index 3c77b3e..744fd6c 100644 --- a/drivers/usb/otg/penwell_otg.c +++ b/drivers/usb/otg/penwell_otg.c @@ -1173,6 +1173,244 @@ static int penwell_otg_manual_chrg_det(void) 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; @@ -1974,11 +2212,13 @@ static void penwell_otg_work(struct work_struct *work) 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) { diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h index d83dee7..38d3eb3 100644 --- a/include/linux/usb/penwell_otg.h +++ b/include/linux/usb/penwell_otg.h @@ -183,6 +183,19 @@ #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 @@ -206,6 +219,22 @@ # 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 @@ -336,13 +365,19 @@ enum msic_vendor { 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 { -- 2.7.4