usb: gadget: xudc: integrate the charger driver
Rakesh Babu Bodla [Fri, 17 Oct 2014 10:32:55 +0000 (15:32 +0530)]
Integrate the tegra charger detection driver with
tegra xudc driver. Also, update the t210
register programming as per latest programming
guide. DCP/CDP/SDP/Non standard chargers detection
will be supported.

Bug 1468463

Change-Id: Ic5a8a6fcc9758eabd9027b9e132d575b1ffb5a12
Signed-off-by: Rakesh Babu Bodla <rbodla@nvidia.com>
Reviewed-on: http://git-master/r/558877
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

drivers/usb/gadget/nvxxx.h
drivers/usb/gadget/nvxxx_udc.c
drivers/usb/gadget/tegra21x_usb_cd.c
drivers/usb/gadget/tegra_usb_cd.c
drivers/usb/gadget/tegra_usb_cd.h
include/linux/usb/tegra_usb_charger.h

index 4820ec4..0f39dde 100644 (file)
 #include <linux/ioctl.h>
 
 extern struct usb_hcd *tegra_xhci_hcd;
+
+#define NON_STD_CHARGER_DET_TIME_MS 1000
+#define USB_ANDROID_SUSPEND_CURRENT_MA 2
+
 /*
  * Register definitions
  */
@@ -634,6 +638,14 @@ struct nv_udc_s {
        struct extcon_dev *id_extcon_dev;
        struct notifier_block id_extcon_nb;
 
+       /* charger detection */
+       struct tegra_usb_cd *ucd;
+       enum tegra_usb_connect_type connect_type;
+       struct work_struct ucd_work;
+       struct work_struct current_work;
+       struct delayed_work non_std_charger_work;
+       u32 current_ma;
+
        /* otg work, will be moved to OTG driver */
        struct work_struct work;
 };
index a16580d..22566b8 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/ioport.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/tegra_usb_charger.h>
 #include <linux/delay.h>
 #include <linux/version.h>
 #include <linux/tegra-powergate.h>
@@ -324,6 +325,56 @@ done:
        return NOTIFY_DONE;
 }
 
+static void tegra_xudc_current_work(struct work_struct *work)
+{
+       struct nv_udc_s *nvudc =
+               container_of(work, struct nv_udc_s, current_work);
+
+       if (nvudc->ucd == NULL)
+               return;
+
+       tegra_ucd_set_sdp_cdp_current(nvudc->ucd, nvudc->current_ma);
+}
+
+static void tegra_xudc_ucd_work(struct work_struct *work)
+{
+       struct nv_udc_s *nvudc =
+               container_of(work, struct nv_udc_s, ucd_work);
+       struct device *dev = nvudc->dev;
+       int ret;
+
+       if (nvudc->ucd == NULL)
+               return;
+
+       if (nvudc->vbus_detected) {
+               nvudc->connect_type =
+                       tegra_ucd_detect_cable_and_set_current(nvudc->ucd);
+               if (nvudc->connect_type == CONNECT_TYPE_SDP)
+                       schedule_delayed_work(&nvudc->non_std_charger_work,
+                               msecs_to_jiffies(NON_STD_CHARGER_DET_TIME_MS));
+               if (!pm_runtime_active(dev)) {
+                       ret = pm_runtime_get(dev);
+                       if (ret)
+                               dev_warn(dev, "Fail to runtime resume device\n");
+               }
+       } else {
+               cancel_delayed_work(&nvudc->non_std_charger_work);
+               tegra_ucd_set_charger_type(nvudc->ucd, CONNECT_TYPE_NONE);
+       }
+}
+
+static void tegra_xudc_non_std_charger_work(struct work_struct *work)
+{
+       struct nv_udc_s *nvudc =
+               container_of(work, struct nv_udc_s, non_std_charger_work.work);
+
+       if (nvudc->ucd == NULL)
+               return;
+
+       tegra_ucd_set_charger_type(nvudc->ucd,
+                       CONNECT_TYPE_NON_STANDARD_CHARGER);
+}
+
 static int extcon_notifications(struct notifier_block *nb,
                                   unsigned long event, void *unused)
 {
@@ -347,6 +398,7 @@ static int extcon_notifications(struct notifier_block *nb,
                vbus_not_detected(nvudc);
        }
 
+       schedule_work(&nvudc->ucd_work);
 exit:
        spin_unlock_irqrestore(&nvudc->lock, flags);
        return NOTIFY_DONE;
@@ -2156,6 +2208,12 @@ static int nvudc_vbus_draw(struct usb_gadget *gadget, unsigned m_a)
 
        nvudc = container_of(gadget, struct nv_udc_s, gadget);
        msg_dbg(nvudc->dev, "nvudc_vbus_draw m_a= 0x%x\n", m_a);
+
+       if (nvudc->current_ma != m_a) {
+               nvudc->current_ma = m_a;
+               schedule_work(&nvudc->current_work);
+       }
+
        return -ENOTSUPP;
 }
 
@@ -3696,6 +3754,7 @@ void nvudc_handle_event(struct nv_udc_s *nvudc, struct event_trb_s *event)
                        u16 seq_num;
                        msg_dbg(nvudc->dev, "nvudc_handle_setup_pkt\n");
 
+                       cancel_delayed_work(&nvudc->non_std_charger_work);
                        setup_pkt = (struct usb_ctrlrequest *)
                                        &event->trb_pointer_lo;
 
@@ -5134,6 +5193,10 @@ static int tegra_xudc_plat_probe(struct platform_device *pdev)
        }
 
        INIT_WORK(&nvudc->work, irq_work);
+       INIT_WORK(&nvudc->ucd_work, tegra_xudc_ucd_work);
+       INIT_WORK(&nvudc->current_work, tegra_xudc_current_work);
+       INIT_DELAYED_WORK(&nvudc->non_std_charger_work,
+                                       tegra_xudc_non_std_charger_work);
 
        nvudc->pdev.plat = pdev;
        nvudc->dev = dev;
@@ -5243,6 +5306,13 @@ static int tegra_xudc_plat_probe(struct platform_device *pdev)
        pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
        pm_runtime_enable(&pdev->dev);
 
+       nvudc->current_ma = USB_ANDROID_SUSPEND_CURRENT_MA;
+       nvudc->ucd = tegra_usb_get_ucd();
+       if (IS_ERR(nvudc->ucd)) {
+               dev_err(dev, "couldn't get charger detection handle\n");
+               nvudc->ucd = NULL;
+       }
+
        /* TODO: support non-dt ?*/
        nvudc->vbus_extcon_dev =
                extcon_get_extcon_dev_by_cable(&pdev->dev, "vbus");
@@ -5288,6 +5358,10 @@ static int __exit tegra_xudc_plat_remove(struct platform_device *pdev)
 
        /* TODO implement synchronization */
        if (nvudc) {
+               tegra_usb_release_ucd(nvudc->ucd);
+               cancel_work_sync(&nvudc->ucd_work);
+               cancel_work_sync(&nvudc->current_work);
+               cancel_delayed_work(&nvudc->non_std_charger_work);
                usb_del_gadget_udc(&nvudc->gadget);
                free_data_struct(nvudc);
                nvudc_plat_clocks_disable(nvudc);
index 0621b41..265079b 100644 (file)
@@ -45,6 +45,7 @@
 
 #define        XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1      0x84
 #define                VBUS_VREG_FIX18                 BIT(6)
+#define                VBUS_VREG_LEV(x)                (((x) & 0x3) << 7)
 #define                USBOP_RPD_OVRD                  BIT(16)
 #define                USBOP_RPD_OVRD_VAL              BIT(17)
 #define                USBOP_RPU_OVRD                  BIT(18)
 
 #define XUSB_PADCTL_USB2_PAD_MUX_0     0x4
 #define                USB2_BIAS_PAD(x)                (((x) & 0x3) << 18)
+#define                USB2_BIAS_PAD_VAL(x)            (((x) & (0x3 << 18)) >> 18)
 #define                USB2_OTG_PAD_PORT0(x)   (((x) & 0x3) << 0)
+#define                USB2_OTG_PAD_PORT0_VAL(x)       (((x) & (0x3 << 0)) >> 0)
 
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0      0x284
 #define                BIAS_PAD_PD     BIT(11)
 
 #define        XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0       0x88
+#define                OTG_PAD0_PD             BIT(26)
 #define                PD2                     BIT(27)
 #define                PD2_OVRD_EN     BIT(28)
 #define                PD_ZI           BIT(29)
@@ -98,7 +102,7 @@ static bool tegra21x_usb_dcd_detect(struct tegra_usb_cd *ucd)
 
        /* Turn on D- pull-down resistor */
        val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1);
-       val |= (USBON_RPD_OVRD | USBON_RPD_OVRD_VAL);
+       val |= (USBON_RPD_OVRD_VAL);
        writel(val, base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1);
 
        /* Wait for TDCD_DBNC */
@@ -139,7 +143,7 @@ static bool tegra21x_usb_dcp_charger_detect(struct tegra_usb_cd *ucd)
 
        /* Data Contact Detection step */
        if (tegra21x_usb_dcd_detect(ucd))
-               dev_info(ucd->dev, "DCD successful\n");
+               DBG(ucd->dev, "DCD successful\n");
 
        /* Primary Detection step */
        /* Source D+ to D- */
@@ -154,12 +158,6 @@ static bool tegra21x_usb_dcp_charger_detect(struct tegra_usb_cd *ucd)
                readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0));
        DBG(ucd->dev, "BATTERY_CHRG_OTGPAD0_CTL1 = %08x",
                readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1));
-       DBG(ucd->dev, "XUSB_PADCTL_USB2_PAD_MUX_0 = 0x%08x\n",
-               readl(base + XUSB_PADCTL_USB2_PAD_MUX_0));
-       DBG(ucd->dev, "XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0 = 0x%08x\n",
-               readl(base + XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0));
-       DBG(ucd->dev, "XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0 = 0x%08x\n",
-               readl(base + XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0));
 
        val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
        if (val & VDAT_DET) {
@@ -171,7 +169,6 @@ static bool tegra21x_usb_dcp_charger_detect(struct tegra_usb_cd *ucd)
        /* Turn off OP_SRC, ON_SINK, clear VDAT, ZIN status change */
        val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
        val &= ~(OP_SRC_EN | ON_SINK_EN);
-       val |= VDAT_DET_ST_CHNG | ZIN_ST_CHNG;
        writel(val, base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
 
        DBG(ucd->dev, "End Status = %d", status);
@@ -205,7 +202,6 @@ static bool tegra21x_usb_cdp_charger_detect(struct tegra_usb_cd *ucd)
        /* Turn off ON_SRC, OP_SINK, clear VDAT, ZIP status change */
        val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
        val &= ~(ON_SRC_EN | OP_SINK_EN);
-       val |= VDAT_DET_ST_CHNG | ZIP_ST_CHNG;
        writel(val, base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
        DBG(ucd->dev, "End Status = %d", status);
        return status;
@@ -226,25 +222,15 @@ static int tegra21x_pad_power_on(struct tegra_usb_cd *ucd)
        unsigned long val;
        void __iomem *base = ucd->regs;
 
-       /* Set bias pad and port0 ownership to XUSB */
        val = readl(base + XUSB_PADCTL_USB2_PAD_MUX_0);
-       val &= ~USB2_BIAS_PAD(~0);
-       val |= USB2_BIAS_PAD(0x1);
-       val &= ~USB2_OTG_PAD_PORT0(~0);
-       val |= USB2_OTG_PAD_PORT0(0x1);
-       writel(val, base + XUSB_PADCTL_USB2_PAD_MUX_0);
-
-       /* Set and clear relavent PD controls */
-       val = readl(base + XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0);
-       val &= ~BIAS_PAD_PD;
-       writel(val, base + XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0);
+       if (USB2_BIAS_PAD_VAL(val) != 0x1 &&
+               USB2_OTG_PAD_PORT0_VAL(val) != 0x1) {
+               dev_err(ucd->dev, "pad/port ownership is not with XUSB");
+               return -EINVAL;
+       }
 
        tegra_pd2_asserted(0);
 
-       val = readl(base + XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0);
-       val &= ~PD_ZI;
-       writel(val, base + XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0);
-
        val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
        val &= ~PD_CHG;
        writel(val, base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0);
@@ -270,13 +256,27 @@ static int tegra21x_pad_power_on(struct tegra_usb_cd *ucd)
 
        tegra21x_usb_charger_filters(base, 1);
 
+       DBG(ucd->dev, "XUSB_PADCTL_USB2_PAD_MUX_0 = 0x%08x\n",
+               readl(base + XUSB_PADCTL_USB2_PAD_MUX_0));
+       DBG(ucd->dev, "XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0 = 0x%08x\n",
+               readl(base + XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0));
+       DBG(ucd->dev, "XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0 = 0x%08x\n",
+               readl(base + XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0));
        return 0;
 }
 
 static int tegra21x_pad_power_off(struct tegra_usb_cd *ucd)
 {
+       void __iomem *base = ucd->regs;
+       unsigned long val;
+
        tegra_pd2_deasserted(0);
 
+       val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1);
+       val &= ~(USBOP_RPD_OVRD | USBOP_RPU_OVRD |
+               USBON_RPD_OVRD | USBON_RPU_OVRD);
+       writel(val, base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1);
+
        tegra21x_usb_charger_filters(ucd->regs, 0);
 
        return 0;
@@ -289,10 +289,24 @@ static void tegra21x_usb_vbus_pad_protection(struct tegra_usb_cd *ucd,
        unsigned long val;
 
        val = readl(base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1);
-       if (enable)
+       if (enable) {
                val &= ~VBUS_VREG_FIX18;
-       else
+               val &= ~VBUS_VREG_LEV(~0);
+               if (ucd->current_limit_ma >= 500 &&
+                               ucd->current_limit_ma <= 899)
+                       val |= VBUS_VREG_LEV(0x0);
+               else if (ucd->current_limit_ma >= 900 &&
+                               ucd->current_limit_ma >= 1499)
+                       val |= VBUS_VREG_LEV(0x1);
+               else if (ucd->current_limit_ma >= 1500 &&
+                               ucd->current_limit_ma >= 1999)
+                       val |= VBUS_VREG_LEV(0x2);
+               else if (ucd->current_limit_ma >= 2000)
+                       val |= VBUS_VREG_LEV(0x3);
+       } else {
                val |= VBUS_VREG_FIX18;
+               val &= ~VBUS_VREG_LEV(~0);
+       }
        writel(val, base + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1);
 }
 
index 903ed8f..5ca0fb3 100644 (file)
@@ -75,6 +75,7 @@ static int tegra_usb_cd_set_current_limit(struct tegra_usb_cd *ucd, int max_ua)
        if (max_ua > 0 && ucd->hw_ops->vbus_pad_protection)
                ucd->hw_ops->vbus_pad_protection(ucd, true);
 
+       dev_info(ucd->dev, "set current %dma\n", max_ua/1000);
        if (ucd->vbus_reg != NULL)
                ret = regulator_set_current_limit(ucd->vbus_reg, 0, max_ua);
 
@@ -86,23 +87,25 @@ static int tegra_usb_cd_set_current_limit(struct tegra_usb_cd *ucd, int max_ua)
 
 static int tegra_usb_cd_set_charging_current(struct tegra_usb_cd *ucd)
 {
-       int max_ua;
+       int max_ua = 0, ret = 0;
+       DBG(ucd->dev, "Begin");
 
        switch (ucd->connect_type) {
        case CONNECT_TYPE_NONE:
                dev_info(ucd->dev, "disconnected USB cable/charger\n");
+               ucd->sdp_cdp_current_limit_ma = 0;
                max_ua = 0;
                break;
        case CONNECT_TYPE_SDP:
-               dev_info(ucd->dev, "connected to SDP\n");
-               if (ucd->current_limit_ma > 2)
+               if (ucd->sdp_cdp_current_limit_ma > 2)
                        dev_info(ucd->dev, "connected to SDP\n");
-               max_ua = min(ucd->current_limit_ma * 1000,
+               max_ua = min(ucd->sdp_cdp_current_limit_ma * 1000,
                                USB_CHARGING_SDP_CURRENT_LIMIT_UA);
                break;
        case CONNECT_TYPE_DCP:
-               dev_info(ucd->dev, "connected to DCP(wall charger)\n");
-               max_ua = ucd->dcp_current_limit_ma * 1000;
+               dev_info(ucd->dev, "connected to DCP\n");
+               max_ua = max(ucd->dcp_current_limit_ma * 1000,
+                               USB_CHARGING_DCP_CURRENT_LIMIT_UA);
                break;
        case CONNECT_TYPE_DCP_QC2:
                dev_info(ucd->dev, "connected to QuickCharge 2(wall charger)\n");
@@ -113,11 +116,11 @@ static int tegra_usb_cd_set_charging_current(struct tegra_usb_cd *ucd)
                max_ua = ucd->dcp_current_limit_ma * 1000;
                break;
        case CONNECT_TYPE_CDP:
-               dev_info(ucd->dev, "connected to CDP(1.5A)\n");
-               if (ucd->current_limit_ma > 2)
+               dev_info(ucd->dev, "connected to CDP\n");
+               if (ucd->sdp_cdp_current_limit_ma > 2)
                        max_ua = USB_CHARGING_CDP_CURRENT_LIMIT_UA;
                else
-                       max_ua = ucd->current_limit_ma * 1000;
+                       max_ua = ucd->sdp_cdp_current_limit_ma * 1000;
                break;
        case CONNECT_TYPE_NON_STANDARD_CHARGER:
                dev_info(ucd->dev, "connected to non-standard charger\n");
@@ -140,7 +143,11 @@ static int tegra_usb_cd_set_charging_current(struct tegra_usb_cd *ucd)
                max_ua = 0;
        }
 
-       return tegra_usb_cd_set_current_limit(ucd, max_ua);
+       ucd->current_limit_ma = max_ua/1000;
+       ret = tegra_usb_cd_set_current_limit(ucd, max_ua);
+
+       DBG(ucd->dev, "End");
+       return ret;
 }
 
 static enum tegra_usb_connect_type
@@ -181,21 +188,34 @@ static enum tegra_usb_connect_type
        else
                ucd->connect_type = CONNECT_TYPE_SDP;
 
+       ucd->hw_ops->power_off(ucd);
+
        tegra_usb_cd_set_extcon_state(ucd);
        tegra_usb_cd_set_charging_current(ucd);
 
-       ucd->hw_ops->power_off(ucd);
-
        DBG(ucd->dev, "End");
-
        return ucd->connect_type;
 }
 
+/* --------------------------- */
+/* API's for controller driver */
+
+void tegra_ucd_set_charger_type(struct tegra_usb_cd *ucd,
+                               enum tegra_usb_connect_type connect_type)
+{
+       ucd->connect_type = connect_type;
+       tegra_usb_cd_set_extcon_state(ucd);
+       tegra_usb_cd_set_charging_current(ucd);
+}
+EXPORT_SYMBOL_GPL(tegra_ucd_set_charger_type);
+
 enum tegra_usb_connect_type
        tegra_ucd_detect_cable_and_set_current(struct tegra_usb_cd *ucd)
 {
-       if (ucd == NULL)
+       if (IS_ERR(ucd)) {
                dev_err(ucd->dev, "ucd not initialized");
+               return -EINVAL;
+       }
 
        return  tegra_usb_cd_detect_cable_and_set_current(ucd);
 }
@@ -203,17 +223,54 @@ EXPORT_SYMBOL_GPL(tegra_ucd_detect_cable_and_set_current);
 
 struct tegra_usb_cd *tegra_usb_get_ucd(void)
 {
+       if (_ucd == NULL)
+               return ERR_PTR(-EINVAL);
+       _ucd->open_count++;
+
        return _ucd;
 }
 EXPORT_SYMBOL_GPL(tegra_usb_get_ucd);
 
+void tegra_usb_release_ucd(struct tegra_usb_cd *ucd)
+{
+       if (ucd == NULL)
+               return;
+
+       ucd->open_count--;
+}
+EXPORT_SYMBOL_GPL(tegra_usb_release_ucd);
+
 void tegra_ucd_set_current(struct tegra_usb_cd *ucd, int current_ma)
 {
+       if (ucd == NULL)
+               return;
+
+       ucd->current_limit_ma = current_ma;
        tegra_usb_cd_set_current_limit(ucd, current_ma*1000);
        return;
 }
 EXPORT_SYMBOL_GPL(tegra_ucd_set_current);
 
+void tegra_ucd_set_sdp_cdp_current(struct tegra_usb_cd *ucd, int current_ma)
+{
+       if (ucd == NULL)
+               return;
+
+       if (ucd->connect_type != CONNECT_TYPE_SDP
+                       && ucd->connect_type != CONNECT_TYPE_CDP) {
+               dev_err(ucd->dev,
+                       "tyring to set current for non SDP/CDP port");
+               return;
+       }
+
+       ucd->sdp_cdp_current_limit_ma = current_ma;
+       tegra_usb_cd_set_charging_current(ucd);
+       return;
+}
+EXPORT_SYMBOL_GPL(tegra_ucd_set_sdp_cdp_current);
+
+/* --------------------------- */
+
 static struct tegra_usb_cd_soc_data tegra_soc_config = {
        .init_hw_ops = tegra21x_usb_cd_init_ops,
 };
@@ -328,6 +385,9 @@ static int tegra_usb_cd_probe(struct platform_device *pdev)
        _ucd = ucd;
        ucd->dev = &pdev->dev;
        ucd->connect_type = CONNECT_TYPE_NONE;
+       ucd->current_limit_ma = 0;
+       ucd->sdp_cdp_current_limit_ma = 0;
+       ucd->open_count = 0;
        soc_data = (struct tegra_usb_cd_soc_data *)match->data;
        platform_set_drvdata(pdev, ucd);
 
@@ -344,8 +404,6 @@ static int tegra_usb_cd_probe(struct platform_device *pdev)
                goto err_iounmap;
        }
 
-       tegra_usb_cd_detect_cable_and_set_current(ucd);
-
        DBG(&pdev->dev, "End");
        return 0;
 
@@ -366,6 +424,9 @@ static int tegra_usb_cd_remove(struct platform_device *pdev)
        if (!ucd)
                return -ENODEV;
 
+       if (ucd->open_count)
+               return -EBUSY;
+
        if (!res) {
                dev_err(ucd->dev, "resource request failed\n");
                return -ENODEV;
index f075d91..ed45d35 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <linux/usb/tegra_usb_charger.h>
 
-#if 1
+#if 0
 #define DBG(dev, fmt, args...) \
                dev_info(dev, "%s():%d, " fmt, __func__, __LINE__, ## args);
 #else
@@ -59,7 +59,9 @@ struct tegra_usb_cd {
        struct extcon_dev *edev;
        struct regulator *vbus_reg;
        void __iomem *regs;
+       int open_count;
        enum tegra_usb_connect_type connect_type;
+       u32 sdp_cdp_current_limit_ma;
        u32 current_limit_ma;
        u32 dcp_current_limit_ma;
        u32 qc2_current_limit_ma;
index f28e3c3..aedfd64 100644 (file)
@@ -30,27 +30,78 @@ enum tegra_usb_connect_type {
        CONNECT_TYPE_DCP_MAXIM,
        CONNECT_TYPE_DCP_QC2,
        CONNECT_TYPE_CDP,
-       CONNECT_TYPE_NV_CHARGER,
        CONNECT_TYPE_NON_STANDARD_CHARGER,
        CONNECT_TYPE_APPLE_500MA,
        CONNECT_TYPE_APPLE_1000MA,
        CONNECT_TYPE_APPLE_2000MA
 };
 
+#if IS_ENABLED(CONFIG_USB_TEGRA_CD)
 /**
  * Returns USB charger detection handle.
  */
-struct tegra_usb_cd *tegra_usb_get_ucd(void);
+extern struct tegra_usb_cd *tegra_usb_get_ucd(void);
+
+/**
+ * Releases USB charger detection handle.
+ */
+extern void tegra_usb_release_ucd(struct tegra_usb_cd *ucd);
 
 /**
  * Detects the USB charger and returns the type.
  */
-enum tegra_usb_connect_type
+extern enum tegra_usb_connect_type
        tegra_ucd_detect_cable_and_set_current(struct tegra_usb_cd *ucd);
 
 /**
- * Set USB charging current.
+ * Set custom USB charging current.
+ */
+extern void tegra_ucd_set_current(struct tegra_usb_cd *ucd, int current_ma);
+
+/**
+ * Set USB charging current for SDP, CDP as per gadget driver.
+ */
+extern void tegra_ucd_set_sdp_cdp_current(struct tegra_usb_cd *ucd,
+               int current_ma);
+
+/**
+ * Set custom USB charger type, this automatically sets the corresponding
+ * charging current.
  */
-void tegra_ucd_set_current(struct tegra_usb_cd *ucd, int current_ma);
+void tegra_ucd_set_charger_type(struct tegra_usb_cd *ucd,
+                       enum tegra_usb_connect_type connect_type);
+#else /* CONFIG_USB_TEGRA_CD */
+
+struct tegra_usb_cd *tegra_usb_get_ucd(void)
+{
+       return  ERR_PTR(-ENODEV);
+}
+
+void tegra_usb_release_ucd(struct tegra_usb_cd *ucd)
+{
+       return;
+}
+
+enum tegra_usb_connect_type
+       tegra_ucd_detect_cable_and_set_current(struct tegra_usb_cd *ucd)
+{
+       return -EINVAL;
+}
+
+void tegra_ucd_set_current(struct tegra_usb_cd *ucd, int current_ma)
+{
+       return;
+}
+
+void tegra_ucd_set_sdp_cdp_current(struct tegra_usb_cd *ucd, int current_ma)
+{
+       return;
+}
 
+void tegra_ucd_set_charger_type(struct tegra_usb_cd *ucd,
+                       enum tegra_usb_connect_type connect_type)
+{
+       return;
+}
+#endif /* CONFIG_USB_TEGRA_CD */
 #endif /* __TEGRA_USB_CHARGER_H */