OTG: usb_multi_charger_detect
Ported from JCsullins 3.0 Kernel
Change-Id: I3908fd6e4fa6c526c1c9bf9d178491d78a3c6022
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index e0a24e5..0a2e7a5 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -586,6 +586,12 @@
indicate if the device should operate in superspeed(=y)
or not.
+config USB_MULTIPLE_CHARGER_DETECT
+ boolean "Multiple USB Wall charger support"
+ depends on USB_GADGET && USB_MSM_72K
+ help
+ Detect multiple types of USB wall charger
+
#
# USB Gadget Drivers
#
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index e6ba74b..b65a93f 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -192,6 +192,10 @@
unsigned chg_current;
unsigned chg_type_retry_cnt;
bool proprietary_chg;
+#ifdef CONFIG_USB_MULTIPLE_CHARGER_DETECT
+ // this flag is to indicate if USB Y-cable(charger + data) is attached.
+ unsigned is_cdp;
+#endif
struct delayed_work chg_det;
struct delayed_work chg_stop;
struct msm_hsusb_gadget_platform_data *pdata;
@@ -238,6 +242,26 @@
static void usb_reset(struct usb_info *ui);
static int usb_ept_set_halt(struct usb_ep *_ep, int value);
+#ifdef CONFIG_USB_MULTIPLE_CHARGER_DETECT
+static int usb_multi_chg_detect(struct usb_info *ui);
+
+static unsigned ulpi_read_with_reset(struct usb_info *ui, unsigned reg)
+{
+ if (ui->xceiv->io_ops->read_with_reset) {
+ return ui->xceiv->io_ops->read_with_reset(ui->xceiv, reg);
+ }
+ return 0;
+}
+
+static int ulpi_write_with_reset(struct usb_info *ui, unsigned val, unsigned reg)
+{
+ if (ui->xceiv->io_ops->write_with_reset) {
+ return ui->xceiv->io_ops->write_with_reset(ui->xceiv, val, reg);
+ }
+ return 0;
+}
+#endif
+
static void msm_hsusb_set_speed(struct usb_info *ui)
{
unsigned long flags;
@@ -328,14 +352,30 @@
spin_lock_irqsave(&ui->lock, flags);
suspended = ui->usb_state == USB_STATE_SUSPENDED ? 1 : 0;
configured = atomic_read(&ui->configured);
+#ifndef CONFIG_USB_MULTIPLE_CHARGER_DETECT
bmaxpow = ui->b_max_pow;
+#else
+ if (ui->is_cdp != 0) {
+ bmaxpow = ui->chg_current;
+ //printk("UDC: it is CDP\n");
+ }
+ else
+ {
+ bmaxpow = ui->b_max_pow;
+ //printk("UDC: it is SDP\n");
+ }
+#endif
spin_unlock_irqrestore(&ui->lock, flags);
if (temp == USB_CHG_TYPE__INVALID)
return -ENODEV;
if (temp == USB_CHG_TYPE__WALLCHARGER && !ui->proprietary_chg)
+#ifndef CONFIG_USB_MULTIPLE_CHARGER_DETECT
return USB_WALLCHARGER_CHG_CURRENT;
+#else
+ return ui->chg_current;
+#endif
else
return USB_PROPRIETARY_CHG_CURRENT;
@@ -441,7 +481,7 @@
spin_unlock_irqrestore(&ui->lock, flags);
return;
}
-
+#ifndef CONFIG_USB_MULTIPLE_CHARGER_DETECT
temp = usb_get_chg_type(ui);
if (temp != USB_CHG_TYPE__WALLCHARGER && temp != USB_CHG_TYPE__SDP
&& !ui->chg_type_retry_cnt) {
@@ -460,7 +500,18 @@
maxpower = usb_get_max_power(ui);
if (maxpower > 0)
usb_phy_set_power(ui->xceiv, maxpower);
+#else
+ spin_unlock_irqrestore(&ui->lock, flags);
+ maxpower = usb_multi_chg_detect(ui);
+ msm72k_pullup_internal(&ui->gadget, 1);
+
+ temp = atomic_read(&otg->chg_type);
+ printk("%s: temp = %d\n", __func__, temp);
+
+ if (maxpower > 0 && temp != USB_CHG_TYPE__SDP )
+ usb_phy_set_power(ui->xceiv, maxpower);
+#endif
/* USB driver prevents idle and suspend power collapse(pc)
* while USB cable is connected. But when dedicated charger is
* connected, driver can vote for idle and suspend pc.
@@ -474,6 +525,106 @@
}
}
+static int usb_multi_chg_detect(struct usb_info *ui)
+{
+#ifdef CONFIG_USB_MULTIPLE_CHARGER_DETECT
+ struct msm_otg *otg = to_msm_otg(ui->xceiv);
+ enum chg_type temp = USB_CHG_TYPE__INVALID;
+ int maxpower = -EINVAL;
+
+ ui->is_cdp = 0; // not CDP
+
+ //disconnect all pull-up and pull-down resistors on D+ and D-
+ writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+ /* S/W workaround, Issue#1 */
+ ulpi_write_with_reset(ui, 0x0f, 0x34);
+ //ulpi_write_with_reset(ui, 0x48, 0x04);
+
+ msleep(10);
+
+ ulpi_write_with_reset(ui,0x4d, 0x04);
+ ulpi_write_with_reset(ui,0x06, 0x0c);
+ msleep(20/*10*/); // this delay must be here !!
+
+ if ((readl(USB_PORTSC) & PORTSC_LS) & (1 << 11)) { //D+ : high
+ if ((readl(USB_PORTSC) & PORTSC_LS) & (1 << 10)) { //D+ : high, D- : high
+ ulpi_write_with_reset(ui, 0x45, 0x04);
+ ulpi_write_with_reset(ui, 0x2, 0x0b); //pull-down D+
+ msleep(10/*100*/);
+// printk("UDC-CHG (2-1): %s (%d) : D+/D- = 0x%x\n", __func__, __LINE__, (readl(USB_PORTSC) & PORTSC_LS));
+ if ((readl(USB_PORTSC) & PORTSC_LS) & (1 << 10)) { //D+ : high, D- : high
+ printk("UDC-CHG (2-1-1-1): %s (%d) : HP Phone Adaptor(900mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__WALLCHARGER;
+ maxpower = 900;//500;
+ } else { //D+ : high, D- : low
+ printk("UDC-CHG (2-1-1-2): %s (%d) : 10W Adaptor(2000mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__WALLCHARGER;
+ maxpower = 2000; //usb_get_max_power(ui);
+
+ }
+ } else { //D+ : high, D- : low
+ printk("UDC-CHG (2-1-2): %s (%d) : Unknown type Adaptor(100mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__WALLCHARGER;
+ maxpower = 100;
+
+ }
+ } else { //D+ : low,
+ if ((readl(USB_PORTSC) & PORTSC_LS) & (1 << 10)) { //D+ : low, D- : high
+ printk("UDC-CHG (2-2): %s (%d) : Unknown type Adaptor(100mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__WALLCHARGER;
+ maxpower = 100;
+
+ } else { //D+ : low, D- : low
+ ulpi_write_with_reset(ui, 0x25, 0x34); //Aplly current source on D+
+ msleep(10/*100*/);
+// printk("UDC-CHG (2-2): %s (%d) : D+/D- = 0x%x\n", __func__, __LINE__, (readl(USB_PORTSC) & PORTSC_LS));
+
+ if ((readl(USB_PORTSC) & PORTSC_LS) & (1 << 11)) { //D+ : high
+ if ((readl(USB_PORTSC) & PORTSC_LS) & (1 << 10)) { //D+ : high, D- : high
+ printk("UDC-CHG (2-2-1-1): %s (%d) : OMTP Phone Adaptor(900mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__WALLCHARGER;
+ maxpower = 900;
+ } else { //D+ : high, D- : low
+ printk("UDC-CHG (2-2-1-2): %s (%d) : Unknown type Adaptor(100mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__WALLCHARGER;
+ maxpower = 100;
+ }
+ } else { //D+ : low
+ ulpi_write_with_reset(ui, 0x24, 0x34);
+ msleep(10);
+ if (ulpi_read_with_reset(ui, 0x34) & (1 << 4)) {
+ ulpi_write_with_reset(ui, 0x0f, 0x34);
+ writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+ msleep(10);
+ writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+ printk("UDC-CHG (2-2-2-1): %s (%d) : USB host Charging Downstream Port(1400mA)!\n", __func__, __LINE__);
+ //temp = USB_CHG_TYPE__WALLCHARGER;
+ temp = USB_CHG_TYPE__SDP;
+ ui->is_cdp = 1;
+ maxpower = 1400;
+
+ } else {
+ ulpi_write_with_reset(ui, 0x0f, 0x34);
+ writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+ msleep(10);
+ writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+ printk("UDC-CHG (2-2-2): %s (%d) : USB host Adaptor(500mA)!\n", __func__, __LINE__);
+ temp = USB_CHG_TYPE__SDP;
+ maxpower = 500;//usb_get_max_power(ui);
+ }
+ }
+ ulpi_write_with_reset(ui,0x0F, 0x34); // clean up current source on D+
+ }
+ }
+
+ atomic_set(&otg->chg_type, temp);
+
+ ui->chg_current = maxpower;
+
+ return maxpower;
+#endif //CONFIG_USB_MULTIPLE_CHARGER_DETECT
+}
+
static int usb_ep_get_stall(struct msm_endpoint *ept)
{
unsigned int n;
@@ -1582,9 +1733,9 @@
if (!atomic_read(&ui->softconnect))
break;
-
+#ifndef CONFIG_USB_MULTIPLE_CHARGER_DETECT
msm72k_pullup_internal(&ui->gadget, 1);
-
+#endif
if (!ui->gadget.is_a_peripheral)
schedule_delayed_work(
&ui->chg_det,
@@ -1733,8 +1884,9 @@
if (!atomic_read(&ui->softconnect))
break;
+#ifndef CONFIG_USB_MULTIPLE_CHARGER_DETECT
msm72k_pullup_internal(&ui->gadget, 1);
-
+#endif
if (!ui->gadget.is_a_peripheral)
schedule_delayed_work(
&ui->chg_det,
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
index c95905e..9a10ff6 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -1424,6 +1424,22 @@
return -1;
}
+#ifdef CONFIG_USB_MULTIPLE_CHARGER_DETECT
+static int usb_ulpi_write_with_reset(struct usb_phy *xceiv, u32 val, u32 reg)
+{
+ struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+ return ulpi_write_with_reset(dev, val, reg);
+}
+
+static int usb_ulpi_read_with_reset(struct usb_phy *xceiv, u32 reg)
+{
+ struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+ return ulpi_read_with_reset(dev, reg);
+}
+#endif
+
/* some of the older targets does not turn off the PLL
* if onclock bit is set and clocksuspendM bit is on,
* hence clear them too and initiate the suspend mode
@@ -2598,6 +2614,10 @@
struct usb_phy_io_ops msm_otg_io_ops = {
.read = usb_ulpi_read,
.write = usb_ulpi_write,
+#ifdef CONFIG_USB_MULTIPLE_CHARGER_DETECT
+ .read_with_reset = usb_ulpi_read_with_reset,
+ .write_with_reset = usb_ulpi_write_with_reset,
+#endif
};
static int __init msm_otg_probe(struct platform_device *pdev)