usb/penwell_otg: add charger detection flow for Clovertrail
authorWu, Hao <hao.wu@intel.com>
Fri, 3 Feb 2012 12:22:25 +0000 (20:22 +0800)
committerbuildbot <buildbot@intel.com>
Wed, 8 Feb 2012 09:02:37 +0000 (01:02 -0800)
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 <hao.wu@intel.com>
Reviewed-on: http://android.intel.com:8080/33883
Reviewed-by: Wang, Feng A <feng.a.wang@intel.com>
Reviewed-by: Tang, Richard <richard.tang@intel.com>
Reviewed-by: Meng, Zhe <zhe.meng@intel.com>
Tested-by: Meng, Zhe <zhe.meng@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
drivers/usb/otg/penwell_otg.c
include/linux/usb/penwell_otg.h

index 3c77b3e..744fd6c 100644 (file)
@@ -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) {
index d83dee7..38d3eb3 100644 (file)
 #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
@@ -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 {