usb: phy: msm: Use extcon framework for VBUS and ID detection
authorIvan T. Ivanov <ivan.ivanov@linaro.org>
Thu, 9 Apr 2015 08:34:22 +0000 (11:34 +0300)
committerFelipe Balbi <balbi@ti.com>
Tue, 28 Apr 2015 16:46:50 +0000 (11:46 -0500)
On recent Qualcomm platforms VBUS and ID lines are not routed to
USB PHY LINK controller. Use extcon framework to receive connect
and disconnect ID and VBUS notification.

Signed-off-by: Ivan T. Ivanov <ivan.ivanov@linaro.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Documentation/devicetree/bindings/usb/msm-hsusb.txt
drivers/usb/phy/Kconfig
drivers/usb/phy/phy-msm-usb.c
include/linux/usb/msm_hsusb.h

index 2826f2af503a2ca79381507b4b583aaae3a23660..f26bcfac3d8fba4a22b993f507df6f92823ade5d 100644 (file)
@@ -69,6 +69,13 @@ Optional properties:
                 (no, min, max) where each value represents either a voltage
                 in microvolts or a value corresponding to voltage corner.
 
+- extcon:       phandles to external connector devices. First phandle
+                should point to external connector, which provide "USB"
+                cable events, the second should point to external connector
+                device, which provide "USB-HOST" cable events. If one of
+                the external connector devices is not required empty <0>
+                phandle should be specified.
+
 Example HSUSB OTG controller device node:
 
     usb@f9a55000 {
index 2175678e674ebf4408635f5582eee50d4bb17400..811f331892d7877451ce6049b67482007b0de09f 100644 (file)
@@ -141,6 +141,7 @@ config USB_MSM_OTG
        tristate "Qualcomm on-chip USB OTG controller support"
        depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST)
        depends on RESET_CONTROLLER
+       depends on EXTCON
        select USB_PHY
        help
          Enable this to support the USB OTG transceiver on Qualcomm chips. It
index c9156beeadef66718e82e002bb53569c64ffdd13..ad66c67ce9a57c92461ef991c70b890016646c43 100644 (file)
@@ -1436,9 +1436,42 @@ static const struct of_device_id msm_otg_dt_match[] = {
 };
 MODULE_DEVICE_TABLE(of, msm_otg_dt_match);
 
+static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
+                               void *ptr)
+{
+       struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb);
+       struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus);
+
+       if (event)
+               set_bit(B_SESS_VLD, &motg->inputs);
+       else
+               clear_bit(B_SESS_VLD, &motg->inputs);
+
+       schedule_work(&motg->sm_work);
+
+       return NOTIFY_DONE;
+}
+
+static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
+                               void *ptr)
+{
+       struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb);
+       struct msm_otg *motg = container_of(id, struct msm_otg, id);
+
+       if (event)
+               clear_bit(ID, &motg->inputs);
+       else
+               set_bit(ID, &motg->inputs);
+
+       schedule_work(&motg->sm_work);
+
+       return NOTIFY_DONE;
+}
+
 static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
 {
        struct msm_otg_platform_data *pdata;
+       struct extcon_dev *ext_id, *ext_vbus;
        const struct of_device_id *id;
        struct device_node *node = pdev->dev.of_node;
        struct property *prop;
@@ -1487,6 +1520,52 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
                motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX];
        }
 
+       ext_id = ERR_PTR(-ENODEV);
+       ext_vbus = ERR_PTR(-ENODEV);
+       if (of_property_read_bool(node, "extcon")) {
+
+               /* Each one of them is not mandatory */
+               ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0);
+               if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
+                       return PTR_ERR(ext_vbus);
+
+               ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1);
+               if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
+                       return PTR_ERR(ext_id);
+       }
+
+       if (!IS_ERR(ext_vbus)) {
+               motg->vbus.nb.notifier_call = msm_otg_vbus_notifier;
+               ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name,
+                                              "USB", &motg->vbus.nb);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "register VBUS notifier failed\n");
+                       return ret;
+               }
+
+               ret = extcon_get_cable_state(ext_vbus, "USB");
+               if (ret)
+                       set_bit(B_SESS_VLD, &motg->inputs);
+               else
+                       clear_bit(B_SESS_VLD, &motg->inputs);
+       }
+
+       if (!IS_ERR(ext_id)) {
+               motg->id.nb.notifier_call = msm_otg_id_notifier;
+               ret = extcon_register_interest(&motg->id.conn, ext_id->name,
+                                              "USB-HOST", &motg->id.nb);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "register ID notifier failed\n");
+                       return ret;
+               }
+
+               ret = extcon_get_cable_state(ext_id, "USB-HOST");
+               if (ret)
+                       clear_bit(ID, &motg->inputs);
+               else
+                       set_bit(ID, &motg->inputs);
+       }
+
        prop = of_find_property(node, "qcom,phy-init-sequence", &len);
        if (!prop || !len)
                return 0;
@@ -1700,6 +1779,11 @@ static int msm_otg_remove(struct platform_device *pdev)
        if (phy->otg->host || phy->otg->gadget)
                return -EBUSY;
 
+       if (motg->id.conn.edev)
+               extcon_unregister_interest(&motg->id.conn);
+       if (motg->vbus.conn.edev)
+               extcon_unregister_interest(&motg->vbus.conn);
+
        msm_otg_debugfs_cleanup();
        cancel_delayed_work_sync(&motg->chg_work);
        cancel_work_sync(&motg->sm_work);
index 7dbecf9a46561f5de2e9904b3b52c31424fb66b1..c4d956e50d09c00e75112658bfd4246ed8a12d62 100644 (file)
@@ -18,6 +18,7 @@
 #ifndef __ASM_ARCH_MSM_HSUSB_H
 #define __ASM_ARCH_MSM_HSUSB_H
 
+#include <linux/extcon.h>
 #include <linux/types.h>
 #include <linux/usb/otg.h>
 #include <linux/clk.h>
@@ -119,6 +120,17 @@ struct msm_otg_platform_data {
        void (*setup_gpio)(enum usb_otg_state state);
 };
 
+/**
+ * struct msm_usb_cable - structure for exteternal connector cable
+ *                       state tracking
+ * @nb: hold event notification callback
+ * @conn: used for notification registration
+ */
+struct msm_usb_cable {
+       struct notifier_block           nb;
+       struct extcon_specific_cable_nb conn;
+};
+
 /**
  * struct msm_otg: OTG driver data. Shared by HCD and DCD.
  * @otg: USB OTG Transceiver structure.
@@ -138,6 +150,8 @@ struct msm_otg_platform_data {
  * @chg_type: The type of charger attached.
  * @dcd_retires: The retry count used to track Data contact
  *               detection process.
+ * @vbus: VBUS signal state trakining, using extcon framework
+ * @id: ID signal state trakining, using extcon framework
  */
 struct msm_otg {
        struct usb_phy phy;
@@ -166,6 +180,9 @@ struct msm_otg {
        struct reset_control *phy_rst;
        struct reset_control *link_rst;
        int vdd_levels[3];
+
+       struct msm_usb_cable vbus;
+       struct msm_usb_cable id;
 };
 
 #endif