summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorXin Xie <xxie@nvidia.com>2013-02-25 17:35:22 -0800
committerSimone Willett <swillett@nvidia.com>2013-03-25 15:27:40 -0700
commit11b0eb2c4f0bd2447445ec2f0bdc6c892e142a54 (patch)
treeafdfd537a1bb89bff83b9afbb333d0b42ecf724a /drivers/usb
parent7d5345bfe041a23462defc92209e812fdcae9f84 (diff)
usb: gadget: tegra: fix NV-charger detection
NV charger is using resistor network to set 2.8v/2.0v for D+/D-. Doing so, we cannot detect SDP and NV-charger reliably. Instead we need: * Implement Data Contact Detection mechanism, so SDP and NV-charger can be detected reliably. * We also need make difference between NV-charger and non-standard charger based on D+/D- line status (non-standard charger D+/D- lines can be pulled high or low individually, NV-charger D+/D- line is always high) bug 1236790 bug 1234552 Change-Id: I1f40aafdcc6a97eb8a8b7fd85392dd9aadf78771 Signed-off-by: Xin Xie <xxie@nvidia.com> Signed-off-by: Anshul Jain <anshulj@nvidia.com> Reviewed-on: http://git-master/r/#change,207217 Reviewed-on: http://git-master/r/210848 GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/tegra_udc.c163
-rw-r--r--drivers/usb/gadget/tegra_udc.h16
2 files changed, 83 insertions, 96 deletions
diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c
index 0c3430fc7553..8577d05aea6f 100644
--- a/drivers/usb/gadget/tegra_udc.c
+++ b/drivers/usb/gadget/tegra_udc.c
@@ -1285,73 +1285,54 @@ static int tegra_set_selfpowered(struct usb_gadget *gadget, int is_on)
udc->selfpowered = (is_on != 0);
return 0;
}
-static void dump_charger_type(struct tegra_udc *udc, int max_ua)
-{
- switch (udc->connect_type) {
- case CONNECT_TYPE_NONE:
- pr_info("cable/charger is not connected\n");
- break;
- case CONNECT_TYPE_SDP:
- pr_info("detected SDP charger, charging current " \
- "limit: %d uA\n", max_ua);
- break;
- case CONNECT_TYPE_DCP:
- pr_info("detected DCP charger, charging current " \
- "limit: %d uA\n", max_ua);
- break;
- case CONNECT_TYPE_CDP:
- pr_info("detected CDP charger, charging current " \
- "limit: %d uA\n", max_ua);
- break;
- case CONNECT_TYPE_NV_CHARGER:
- pr_info("detected NV charger, charging current " \
- "limit: %d uA\n", max_ua);
- break;
- case CONNECT_TYPE_NON_STANDARD_CHARGER:
- pr_info("detected non-standard charger, charging" \
- "current limit %d uA\n", max_ua);
- break;
- default:
- pr_info("Detected USB charging type is unknown\n");
- }
-}
+
static int tegra_usb_set_charging_current(struct tegra_udc *udc)
{
int max_ua;
+ struct device *dev;
- if (NULL == udc->vbus_reg)
- return 0;
-
+ dev = &udc->pdev->dev;
switch (udc->connect_type) {
case CONNECT_TYPE_NONE:
- pr_debug("cable/charger is not connected");
+ dev_info(dev, "cable/charger is not connected\n");
max_ua = 0;
break;
case CONNECT_TYPE_SDP:
- pr_debug("detected SDP port");
- max_ua = USB_CHARGING_SDP_CURRENT_LIMIT_UA;
+ dev_info(dev, "detected SDP port\n");
+ max_ua = min(udc->current_limit * 1000,
+ USB_CHARGING_SDP_CURRENT_LIMIT_UA);
break;
case CONNECT_TYPE_DCP:
- pr_debug("detected DCP port(wall charger)");
+ dev_info(dev, "detected DCP port(wall charger)\n");
max_ua = USB_CHARGING_DCP_CURRENT_LIMIT_UA;
break;
case CONNECT_TYPE_CDP:
- pr_debug("detected CDP port(1A USB port)");
- max_ua = USB_CHARGING_CDP_CURRENT_LIMIT_UA;
+ dev_info(dev, "detected CDP port(1A USB port)\n");
+ /*
+ * if current is more than VBUS suspend current, we draw CDP
+ * allowed maximum current (override SDP max current which is
+ * set by the upper level driver).
+ */
+ if (udc->current_limit > 2)
+ max_ua = USB_CHARGING_CDP_CURRENT_LIMIT_UA;
+ else
+ max_ua = udc->current_limit * 1000;
break;
case CONNECT_TYPE_NV_CHARGER:
- pr_debug("detected NV charging port");
+ dev_info(dev, "detected NV charging port\n");
max_ua = USB_CHARGING_NV_CHARGER_CURRENT_LIMIT_UA;
break;
case CONNECT_TYPE_NON_STANDARD_CHARGER:
- pr_debug("detected non-standard charging port");
+ dev_info(dev, "detected non-standard charging port\n");
max_ua = USB_CHARGING_NON_STANDARD_CHARGER_CURRENT_LIMIT_UA;
break;
default:
- pr_debug("detected USB charging type is unknown");
+ dev_info(dev, "detected USB charging type is unknown\n");
max_ua = 0;
}
- dump_charger_type(udc, max_ua);
+
+ if (NULL == udc->vbus_reg)
+ return 0;
return regulator_set_current_limit(udc->vbus_reg, 0, max_ua);
}
@@ -1391,6 +1372,26 @@ static void tegra_detect_charging_type_is_cdp_or_dcp(struct tegra_udc *udc)
spin_unlock_irqrestore(&udc->lock, flags);
}
+static int tegra_detect_cable_type(struct tegra_udc *udc)
+{
+ if (tegra_usb_phy_charger_detected(udc->phy))
+ tegra_detect_charging_type_is_cdp_or_dcp(udc);
+ else if (tegra_usb_phy_nv_charger_detected(udc->phy))
+ udc->connect_type = CONNECT_TYPE_NV_CHARGER;
+ else
+ udc->connect_type = CONNECT_TYPE_SDP;
+
+ /*
+ * If it is charger types, we start charging now. If it connect to USB
+ * host, we let upper gadget driver to decide the current capability.
+ */
+ if ((udc->connect_type != CONNECT_TYPE_SDP) &&
+ (udc->connect_type != CONNECT_TYPE_CDP))
+ tegra_usb_set_charging_current(udc);
+
+ return 0;
+}
+
/**
* Notify controller that VBUS is powered, called by whatever
* detects VBUS sessions
@@ -1404,7 +1405,7 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active)
if (udc->vbus_active && !is_active) {
/* If cable disconnected, cancel any delayed work */
- cancel_delayed_work(&udc->non_std_charger_work);
+ cancel_delayed_work_sync(&udc->non_std_charger_work);
spin_lock_irqsave(&udc->lock, flags);
/* reset all internal Queues and inform client driver */
reset_queues(udc);
@@ -1428,26 +1429,11 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active)
udc->ep0_state = WAIT_FOR_SETUP;
udc->ep0_dir = 0;
udc->vbus_active = 1;
- if (tegra_usb_phy_charger_detected(udc->phy)) {
- tegra_detect_charging_type_is_cdp_or_dcp(udc);
- } else {
- 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);
- if (udc->connect_type != CONNECT_TYPE_SDP)
- tegra_usb_set_charging_current(udc);
+ tegra_detect_cable_type(udc);
+ /* start the controller if USB host detected */
+ if ((udc->connect_type == CONNECT_TYPE_SDP) ||
+ (udc->connect_type == CONNECT_TYPE_CDP))
+ dr_controller_run(udc);
}
return 0;
@@ -1466,11 +1452,9 @@ static int tegra_vbus_draw(struct usb_gadget *gadget, unsigned mA)
struct tegra_udc *udc;
udc = container_of(gadget, struct tegra_udc, gadget);
- /* Do not set current limits for CDP ports */
- if (udc->connect_type == CONNECT_TYPE_SDP) {
- udc->current_limit = mA;
- schedule_work(&udc->current_work);
- }
+
+ udc->current_limit = mA;
+ schedule_work(&udc->current_work);
if (udc->transceiver)
return usb_phy_set_power(udc->transceiver, mA);
@@ -1493,13 +1477,24 @@ static int tegra_pullup(struct usb_gadget *gadget, int is_on)
return 0;
tmp = udc_readl(udc, USB_CMD_REG_OFFSET);
- if (can_pullup(udc))
- udc_writel(udc, tmp | USB_CMD_RUN_STOP,
- USB_CMD_REG_OFFSET);
- else
- udc_writel(udc, (tmp & ~USB_CMD_RUN_STOP),
- USB_CMD_REG_OFFSET);
+ if (can_pullup(udc)) {
+ udc_writel(udc, tmp | USB_CMD_RUN_STOP, USB_CMD_REG_OFFSET);
+ /*
+ * We cannot tell difference between a SDP and non-standard
+ * charger (which has D+/D- line floating) based on line status
+ * at the time VBUS is detected.
+ *
+ * We can schedule a 4s delayed work and verify it is an
+ * non-standard charger if no setup packet is received after
+ * enumeration started.
+ */
+ if (udc->connect_type == CONNECT_TYPE_SDP)
+ schedule_delayed_work(&udc->non_std_charger_work,
+ msecs_to_jiffies(NON_STD_CHARGER_DET_TIME_MS));
+ } else {
+ udc_writel(udc, (tmp & ~USB_CMD_RUN_STOP), USB_CMD_REG_OFFSET);
+ }
return 0;
}
@@ -1827,10 +1822,6 @@ static void setup_received_irq(struct tegra_udc *udc,
return;
case USB_REQ_SET_ADDRESS:
- if (udc->connect_type != CONNECT_TYPE_CDP)
- udc->connect_type = CONNECT_TYPE_SDP;
- tegra_vbus_draw(&udc->gadget,
- USB_CHARGING_SDP_CURRENT_LIMIT_UA/1000);
/* Status phase from udc */
if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
| USB_RECIP_DEVICE))
@@ -2254,14 +2245,7 @@ static void tegra_udc_set_current_limit_work(struct work_struct *work)
{
struct tegra_udc *udc = container_of(work, struct tegra_udc,
current_work);
- /* check udc regulator is available for drawing vbus current*/
- if (udc->vbus_reg) {
- /* set the current limit in uA */
- dump_charger_type(udc, udc->current_limit * 1000);
- regulator_set_current_limit(
- udc->vbus_reg, 0,
- udc->current_limit * 1000);
- }
+ tegra_usb_set_charging_current(udc);
}
#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
@@ -2297,7 +2281,7 @@ static void tegra_udc_irq_work(struct work_struct *irq_work)
/*
* When VBUS is detected we already know it is DCP/SDP/CDP devices if it is a
* standard device; If we did not receive EP0 setup packet, we can assuming it
- * is a charger capable of 1.8A charging.
+ * is an non-DCP charger.
*/
static void tegra_udc_non_std_charger_detect_work(struct work_struct *work)
{
@@ -2305,7 +2289,10 @@ static void tegra_udc_non_std_charger_detect_work(struct work_struct *work)
non_std_charger_work.work);
DBG("%s(%d) BEGIN\n", __func__, __LINE__);
+ dr_controller_stop(udc);
+
udc->connect_type = CONNECT_TYPE_NON_STANDARD_CHARGER;
+
tegra_usb_set_charging_current(udc);
DBG("%s(%d) END\n", __func__, __LINE__);
diff --git a/drivers/usb/gadget/tegra_udc.h b/drivers/usb/gadget/tegra_udc.h
index 954a0ca5f8c8..6659ed0e0f5f 100644
--- a/drivers/usb/gadget/tegra_udc.h
+++ b/drivers/usb/gadget/tegra_udc.h
@@ -38,14 +38,14 @@
#define USB_MAX_CTRL_PAYLOAD 64
/* Charger current limit=1800mA, as per the USB charger spec */
-#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 500000
-
- /* 4 sec wait time for charger detection after vbus is detected */
-#define USB_CHARGER_DETECTION_WAIT_TIME_MS 4000
+#define USB_CHARGING_DCP_CURRENT_LIMIT_UA 1800000u
+#define USB_CHARGING_CDP_CURRENT_LIMIT_UA 1500000u
+#define USB_CHARGING_SDP_CURRENT_LIMIT_UA 500000u
+#define USB_CHARGING_NV_CHARGER_CURRENT_LIMIT_UA 2000000u
+#define USB_CHARGING_NON_STANDARD_CHARGER_CURRENT_LIMIT_UA 500000u
+
+ /* 4 sec wait time for non-std charger detection after vbus is detected */
+#define NON_STD_CHARGER_DET_TIME_MS 4000
#define BOOST_TRIGGER_SIZE 4096
#define UDC_RESET_TIMEOUT_MS 1000