usb: gadget: tegra: add NV charger detection
Rakesh Bodla [Wed, 23 Jan 2013 15:19:33 +0000 (20:19 +0530)]
Adding the support to detect NV charger and
set current limit to withdraw as 2.0amps.

Bug 1193528

Change-Id: I5ef6c553b33dfa45bfd9e07a9ba05ddf3c75e239
Signed-off-by: Rakesh Bodla <rbodla@nvidia.com>
Reviewed-on: http://git-master/r/192055
Reviewed-by: Riham Haidar <rhaidar@nvidia.com>
Tested-by: Riham Haidar <rhaidar@nvidia.com>

arch/arm/mach-tegra/include/mach/usb_phy.h
arch/arm/mach-tegra/tegra11x_usb_phy.c
arch/arm/mach-tegra/tegra_usb_phy.h
arch/arm/mach-tegra/usb_phy.c
drivers/usb/gadget/tegra_udc.c
drivers/usb/gadget/tegra_udc.h

index 2db26c2..9413c46 100644 (file)
@@ -97,6 +97,12 @@ bool tegra_usb_phy_hw_accessible(struct tegra_usb_phy *phy);
 bool tegra_usb_phy_charger_detected(struct tegra_usb_phy *phy);
 
 /**
+ * Indicates whether nvidia proprietary charger is connected or not
+ * if nvidia proprietary charger is detected then returns true else false
+ */
+bool tegra_usb_phy_nv_charger_detected(struct tegra_usb_phy *phy);
+
+/**
  * Indicates whether phy resumed due to the pmc remote/hotplug wake event
  *  or not, returns true if remote/hotplug wake is detected.
  */
index cddfdc1..388f6cc 100644 (file)
@@ -75,6 +75,7 @@
 #define   USB_PORTSC_CCS       (1 << 0)
 #define   USB_PORTSC_RWC_BITS (USB_PORTSC_CSC | USB_PORTSC_PEC | USB_PORTSC_OCC)
 #define   USB_PORTSC_PSPD_MASK 3
+#define   USB_PORTSC_LINE_STATE(x) (((x) & (0x3 << 10)) >> 10)
 
 #define HOSTPC1_DEVLC          0x1b4
 #define   HOSTPC1_DEVLC_PHCD           (1 << 22)
 #define   UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
 #define   UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
 #define   UTMIP_SUSPEND_EXIT_ON_EDGE   (1 << 22)
-#define   FORCE_PULLDN_DM      (1 << 8)
+#define   DISABLE_PULLUP_DP (1 << 15)
+#define   DISABLE_PULLUP_DM (1 << 14)
+#define   DISABLE_PULLDN_DP (1 << 13)
+#define   DISABLE_PULLDN_DM (1 << 12)
+#define   FORCE_PULLUP_DP      (1 << 11)
+#define   FORCE_PULLUP_DM      (1 << 10)
 #define   FORCE_PULLDN_DP      (1 << 9)
+#define   FORCE_PULLDN_DM      (1 << 8)
 #define   COMB_TERMS           (1 << 0)
 #define   ALWAYS_FREE_RUNNING_TERMS (1 << 1)
+#define   MASK_ALL_PULLUP_PULLDOWN (0xff << 8)
 
 #define UTMIP_SPARE_CFG0       0x834
 #define   FUSE_SETUP_SEL               (1 << 3)
@@ -1800,6 +1808,61 @@ static bool utmi_phy_charger_detect(struct tegra_usb_phy *phy)
        return status;
 }
 
+static bool utmi_phy_nv_charger_detect(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *base = phy->regs;
+       bool status;
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+       /* Turn off all terminations so we see DPDN states clearly,
+               and only leave on DM pulldown */
+       val = readl(base + UTMIP_MISC_CFG0);
+       val &= ~MASK_ALL_PULLUP_PULLDOWN;
+       val |= (DISABLE_PULLUP_DP | DISABLE_PULLUP_DM
+                       | FORCE_PULLDN_DP | DISABLE_PULLDN_DM);
+       writel(val, base + UTMIP_MISC_CFG0);
+
+       udelay(100);
+
+       val = readl(base + USB_PORTSC);
+       status = (USB_PORTSC_LINE_STATE(val) == 0);
+
+       /* Turn off all terminations except for DP pullup */
+       val = readl(base + UTMIP_MISC_CFG0);
+       val &= ~MASK_ALL_PULLUP_PULLDOWN;
+       val |= (FORCE_PULLUP_DP | DISABLE_PULLUP_DM
+                       | DISABLE_PULLDN_DP | DISABLE_PULLDN_DM);
+       writel(val, base + UTMIP_MISC_CFG0);
+
+       udelay(100);
+
+       val = readl(base + USB_PORTSC);
+       if (status & (USB_PORTSC_LINE_STATE(val) == 0x3)) {
+               status = false;
+       } else {
+               status = false;
+
+               /* Check for NV charger DISABLE all terminations */
+               val = readl(base + UTMIP_MISC_CFG0);
+               val &= ~MASK_ALL_PULLUP_PULLDOWN;
+               val |= (DISABLE_PULLUP_DP | DISABLE_PULLUP_DM
+                               | DISABLE_PULLDN_DP | DISABLE_PULLDN_DM);
+               writel(val, base + UTMIP_MISC_CFG0);
+
+               val = readl(base + USB_PORTSC);
+               if (USB_PORTSC_LINE_STATE(val) == 0x3)
+                       status = true;
+       }
+
+       /* Restore standard termination by hardware. */
+       val = readl(base + UTMIP_MISC_CFG0);
+       val &= ~MASK_ALL_PULLUP_PULLDOWN;
+       writel(val, base + UTMIP_MISC_CFG0);
+
+       return status;
+}
+
 static void uhsic_powerup_pmc_wake_detect(struct tegra_usb_phy *phy)
 {
        unsigned long val;
@@ -3107,6 +3170,7 @@ static struct tegra_usb_phy_ops utmi_phy_ops = {
        .pre_resume = utmi_phy_pre_resume,
        .resume = utmi_phy_resume,
        .charger_detect = utmi_phy_charger_detect,
+       .nv_charger_detect = utmi_phy_nv_charger_detect,
 };
 
 static struct tegra_usb_phy_ops uhsic_phy_ops = {
index ef7fd79..f4058b5 100644 (file)
@@ -67,6 +67,7 @@ struct tegra_usb_phy_ops {
        int (*power_off)(struct tegra_usb_phy *phy);
        int (*power_on)(struct tegra_usb_phy *phy);
        bool (*charger_detect)(struct tegra_usb_phy *phy);
+       bool (*nv_charger_detect)(struct tegra_usb_phy *phy);
 };
 
 /**
index 387e29a..40ba21b 100644 (file)
@@ -572,6 +572,17 @@ bool tegra_usb_phy_charger_detected(struct tegra_usb_phy *phy)
        return status;
 }
 
+bool tegra_usb_phy_nv_charger_detected(struct tegra_usb_phy *phy)
+{
+       bool status = 0;
+
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+       if (phy->ops && phy->ops->nv_charger_detect)
+               status = phy->ops->nv_charger_detect(phy);
+
+       return status;
+}
+
 bool tegra_usb_phy_hw_accessible(struct tegra_usb_phy *phy)
 {
        if (!phy->hw_accessible)
index c316091..6826e0b 100644 (file)
@@ -1309,6 +1309,10 @@ static int tegra_usb_set_charging_current(struct tegra_udc *udc)
                pr_debug("detected CDP port(1A USB port)");
                max_ua = USB_CHARGING_CDP_CURRENT_LIMIT_UA;
                break;
+       case CONNECT_TYPE_NV_CHARGER:
+               pr_debug("detected NV charging port");
+               max_ua = USB_CHARGING_NV_CHARGER_CURRENT_LIMIT_UA;
+               break;
        case CONNECT_TYPE_NON_STANDARD_CHARGER:
                pr_debug("detected non-standard charging port");
                max_ua = USB_CHARGING_NON_STANDARD_CHARGER_CURRENT_LIMIT_UA;
@@ -1397,15 +1401,18 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active)
                if (tegra_usb_phy_charger_detected(udc->phy)) {
                        tegra_detect_charging_type_is_cdp_or_dcp(udc);
                } else {
-                       udc->connect_type = CONNECT_TYPE_SDP;
-                       /*
-                        * Schedule work to wait for 1000 msec and check for
-                        * a non-standard charger if setup packet is not
-                        * received.
-                        */
-                       schedule_delayed_work(&udc->non_std_charger_work,
-                               msecs_to_jiffies(
-                               USB_CHARGER_DETECTION_WAIT_TIME_MS));
+                       if (tegra_usb_phy_nv_charger_detected(udc->phy)) {
+                               udc->connect_type = CONNECT_TYPE_NV_CHARGER;
+                       } else {
+                               udc->connect_type = CONNECT_TYPE_SDP;
+                               /* Schedule work to wait for 4000 msec and check
+                                * for a non-standard charger if setup packet is
+                                * not received.
+                                */
+                               schedule_delayed_work(&udc->non_std_charger_work
+                                       , msecs_to_jiffies(
+                                       USB_CHARGER_DETECTION_WAIT_TIME_MS));
+                       }
                }
                /* start the controller */
                dr_controller_run(udc);
index ac609ed..d90d462 100644 (file)
 #define USB_CHARGING_DCP_CURRENT_LIMIT_UA 1800000
 #define USB_CHARGING_CDP_CURRENT_LIMIT_UA 1500000
 #define USB_CHARGING_SDP_CURRENT_LIMIT_UA 100000
+#define USB_CHARGING_NV_CHARGER_CURRENT_LIMIT_UA 2000000
 #define USB_CHARGING_NON_STANDARD_CHARGER_CURRENT_LIMIT_UA 1800000
 
- /* 1 sec wait time for charger detection after vbus is detected */
-#define USB_CHARGER_DETECTION_WAIT_TIME_MS 1000
+ /* 4 sec wait time for charger detection after vbus is detected */
+#define USB_CHARGER_DETECTION_WAIT_TIME_MS 4000
 #define BOOST_TRIGGER_SIZE 4096
 
 #define UDC_RESET_TIMEOUT_MS 1000
@@ -409,6 +410,7 @@ enum tegra_connect_type {
        CONNECT_TYPE_SDP,
        CONNECT_TYPE_DCP,
        CONNECT_TYPE_CDP,
+       CONNECT_TYPE_NV_CHARGER,
        CONNECT_TYPE_NON_STANDARD_CHARGER
 };