Merge remote-tracking branch 'usb-gadget/next'
[deliverable/linux.git] / drivers / usb / gadget / udc / pxa27x_udc.c
index ad140aa00132eb73dc9d490c5eb06e1b79190878..7fa60f5b7ae408b39461107954d561bc303926af 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/usb.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/phy.h>
 
 #include "pxa27x_udc.h"
 
@@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
        return -EOPNOTSUPP;
 }
 
+/**
+ * pxa_udc_phy_event - Called by phy upon VBus event
+ * @nb: notifier block
+ * @action: phy action, is vbus connect or disconnect
+ * @data: the usb_gadget structure in pxa_udc
+ *
+ * Called by the USB Phy when a cable connect or disconnect is sensed.
+ *
+ * Returns 0
+ */
+static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action,
+                            void *data)
+{
+       struct usb_gadget *gadget = data;
+
+       switch (action) {
+       case USB_EVENT_VBUS:
+               usb_gadget_vbus_connect(gadget);
+               return NOTIFY_OK;
+       case USB_EVENT_NONE:
+               usb_gadget_vbus_disconnect(gadget);
+               return NOTIFY_OK;
+       default:
+               return NOTIFY_DONE;
+       }
+}
+
+static struct notifier_block pxa27x_udc_phy = {
+       .notifier_call = pxa_udc_phy_event,
+};
+
 static int pxa27x_udc_start(struct usb_gadget *g,
                struct usb_gadget_driver *driver);
 static int pxa27x_udc_stop(struct usb_gadget *g);
@@ -2432,7 +2464,14 @@ static int pxa_udc_probe(struct platform_device *pdev)
                return udc->irq;
 
        udc->dev = &pdev->dev;
-       udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (of_have_populated_dt()) {
+               udc->transceiver =
+                       devm_usb_get_phy_by_phandle(udc->dev, "phys", 0);
+               if (IS_ERR(udc->transceiver))
+                       return PTR_ERR(udc->transceiver);
+       } else {
+               udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+       }
 
        if (IS_ERR(udc->gpiod)) {
                dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
@@ -2465,14 +2504,20 @@ static int pxa_udc_probe(struct platform_device *pdev)
                goto err;
        }
 
+       if (!IS_ERR_OR_NULL(udc->transceiver))
+               usb_register_notifier(udc->transceiver, &pxa27x_udc_phy);
        retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
        if (retval)
-               goto err;
+               goto err_add_gadget;
 
        pxa_init_debugfs(udc);
        if (should_enable_udc(udc))
                udc_enable(udc);
        return 0;
+
+err_add_gadget:
+       if (!IS_ERR_OR_NULL(udc->transceiver))
+               usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
 err:
        clk_unprepare(udc->clk);
        return retval;
@@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev)
        usb_del_gadget_udc(&udc->gadget);
        pxa_cleanup_debugfs(udc);
 
+       if (!IS_ERR_OR_NULL(udc->transceiver))
+               usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
        usb_put_phy(udc->transceiver);
 
        udc->transceiver = NULL;
This page took 0.029701 seconds and 5 git commands to generate.