|
@@ -33,6 +33,7 @@
|
|
#include <linux/usb.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/ch9.h>
|
|
#include <linux/usb/ch9.h>
|
|
#include <linux/usb/gadget.h>
|
|
#include <linux/usb/gadget.h>
|
|
|
|
+#include <linux/usb/phy.h>
|
|
|
|
|
|
#include "pxa27x_udc.h"
|
|
#include "pxa27x_udc.h"
|
|
|
|
|
|
@@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
|
return -EOPNOTSUPP;
|
|
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,
|
|
static int pxa27x_udc_start(struct usb_gadget *g,
|
|
struct usb_gadget_driver *driver);
|
|
struct usb_gadget_driver *driver);
|
|
static int pxa27x_udc_stop(struct usb_gadget *g);
|
|
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;
|
|
return udc->irq;
|
|
|
|
|
|
udc->dev = &pdev->dev;
|
|
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)) {
|
|
if (IS_ERR(udc->gpiod)) {
|
|
dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
|
|
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;
|
|
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);
|
|
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
|
|
if (retval)
|
|
if (retval)
|
|
- goto err;
|
|
|
|
|
|
+ goto err_add_gadget;
|
|
|
|
|
|
pxa_init_debugfs(udc);
|
|
pxa_init_debugfs(udc);
|
|
if (should_enable_udc(udc))
|
|
if (should_enable_udc(udc))
|
|
udc_enable(udc);
|
|
udc_enable(udc);
|
|
return 0;
|
|
return 0;
|
|
|
|
+
|
|
|
|
+err_add_gadget:
|
|
|
|
+ if (!IS_ERR_OR_NULL(udc->transceiver))
|
|
|
|
+ usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
|
|
err:
|
|
err:
|
|
clk_unprepare(udc->clk);
|
|
clk_unprepare(udc->clk);
|
|
return retval;
|
|
return retval;
|
|
@@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev)
|
|
usb_del_gadget_udc(&udc->gadget);
|
|
usb_del_gadget_udc(&udc->gadget);
|
|
pxa_cleanup_debugfs(udc);
|
|
pxa_cleanup_debugfs(udc);
|
|
|
|
|
|
|
|
+ if (!IS_ERR_OR_NULL(udc->transceiver))
|
|
|
|
+ usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
|
|
usb_put_phy(udc->transceiver);
|
|
usb_put_phy(udc->transceiver);
|
|
|
|
|
|
udc->transceiver = NULL;
|
|
udc->transceiver = NULL;
|