usb: gadget: tegra: add NV charger detection
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>
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index 2db26c2..9413c46 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -97,6 +97,12 @@
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.
*/
diff --git a/arch/arm/mach-tegra/tegra11x_usb_phy.c b/arch/arm/mach-tegra/tegra11x_usb_phy.c
index cddfdc1..388f6cc 100644
--- a/arch/arm/mach-tegra/tegra11x_usb_phy.c
+++ b/arch/arm/mach-tegra/tegra11x_usb_phy.c
@@ -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)
@@ -218,10 +219,17 @@
#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 @@
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 @@
.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 = {
diff --git a/arch/arm/mach-tegra/tegra_usb_phy.h b/arch/arm/mach-tegra/tegra_usb_phy.h
index ef7fd79..f4058b5 100644
--- a/arch/arm/mach-tegra/tegra_usb_phy.h
+++ b/arch/arm/mach-tegra/tegra_usb_phy.h
@@ -67,6 +67,7 @@
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);
};
/**
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 387e29a..40ba21b 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -572,6 +572,17 @@
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)
diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c
index c316091..6826e0b 100644
--- a/drivers/usb/gadget/tegra_udc.c
+++ b/drivers/usb/gadget/tegra_udc.c
@@ -1309,6 +1309,10 @@
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 @@
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);
diff --git a/drivers/usb/gadget/tegra_udc.h b/drivers/usb/gadget/tegra_udc.h
index ac609ed..d90d462 100644
--- a/drivers/usb/gadget/tegra_udc.h
+++ b/drivers/usb/gadget/tegra_udc.h
@@ -41,10 +41,11 @@
#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 @@
CONNECT_TYPE_SDP,
CONNECT_TYPE_DCP,
CONNECT_TYPE_CDP,
+ CONNECT_TYPE_NV_CHARGER,
CONNECT_TYPE_NON_STANDARD_CHARGER
};