aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Jarzmik <robert.jarzmik@free.fr>2016-07-08 16:09:05 -0400
committerFelipe Balbi <felipe.balbi@linux.intel.com>2016-08-25 05:13:06 -0400
commit0c0e287d454a2dbfaea4159ed45f7b146010f88b (patch)
treeac8d364b570c2461837c80421e7e03289a5e7ac6
parentdc55c67e9c95e89ef5e384fb1f70520a5383c9dd (diff)
usb: gadget: pxa27x: add phy notifier event handler
In the legacy behavior, and USB phy, upon detection a VBus signal, was calling usb_gadget_vbus_(dis)connect(). This model doesn't work if the phy is generic and doesn't have an adherence to the gadget API. Instead of relying on the phy to call the gadget API, hook up the phy notifier to report the VBus event, and upon it call the usb gadget API ourselves. This brings a new ordering problem, as before even if the usb_get_phy() was failing because the UDC was probed before the phy, the phy would call the gadget anyway, making the VBus connection event forwarded to the gadget. Now we rely on the notifier, we have to ensure the xxx_get_phy() does indeed work. In order to cope with this, it is assumed that : - for legacy platform_data machine, as the ordering cannot be ensured, the phy must call usb_gadget_vbus_(dis)connect, such as phy-gpio-vbus-usb.c - for new devicetree platforms, we'll rely on the probe deferral, and the phy can be gadget API agnostic. Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c51
1 files changed, 49 insertions, 2 deletions
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index ad140aa00132..7fa60f5b7ae4 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -33,6 +33,7 @@
33#include <linux/usb.h> 33#include <linux/usb.h>
34#include <linux/usb/ch9.h> 34#include <linux/usb/ch9.h>
35#include <linux/usb/gadget.h> 35#include <linux/usb/gadget.h>
36#include <linux/usb/phy.h>
36 37
37#include "pxa27x_udc.h" 38#include "pxa27x_udc.h"
38 39
@@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
1655 return -EOPNOTSUPP; 1656 return -EOPNOTSUPP;
1656} 1657}
1657 1658
1659/**
1660 * pxa_udc_phy_event - Called by phy upon VBus event
1661 * @nb: notifier block
1662 * @action: phy action, is vbus connect or disconnect
1663 * @data: the usb_gadget structure in pxa_udc
1664 *
1665 * Called by the USB Phy when a cable connect or disconnect is sensed.
1666 *
1667 * Returns 0
1668 */
1669static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action,
1670 void *data)
1671{
1672 struct usb_gadget *gadget = data;
1673
1674 switch (action) {
1675 case USB_EVENT_VBUS:
1676 usb_gadget_vbus_connect(gadget);
1677 return NOTIFY_OK;
1678 case USB_EVENT_NONE:
1679 usb_gadget_vbus_disconnect(gadget);
1680 return NOTIFY_OK;
1681 default:
1682 return NOTIFY_DONE;
1683 }
1684}
1685
1686static struct notifier_block pxa27x_udc_phy = {
1687 .notifier_call = pxa_udc_phy_event,
1688};
1689
1658static int pxa27x_udc_start(struct usb_gadget *g, 1690static int pxa27x_udc_start(struct usb_gadget *g,
1659 struct usb_gadget_driver *driver); 1691 struct usb_gadget_driver *driver);
1660static int pxa27x_udc_stop(struct usb_gadget *g); 1692static int pxa27x_udc_stop(struct usb_gadget *g);
@@ -2432,7 +2464,14 @@ static int pxa_udc_probe(struct platform_device *pdev)
2432 return udc->irq; 2464 return udc->irq;
2433 2465
2434 udc->dev = &pdev->dev; 2466 udc->dev = &pdev->dev;
2435 udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); 2467 if (of_have_populated_dt()) {
2468 udc->transceiver =
2469 devm_usb_get_phy_by_phandle(udc->dev, "phys", 0);
2470 if (IS_ERR(udc->transceiver))
2471 return PTR_ERR(udc->transceiver);
2472 } else {
2473 udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
2474 }
2436 2475
2437 if (IS_ERR(udc->gpiod)) { 2476 if (IS_ERR(udc->gpiod)) {
2438 dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n", 2477 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)
2465 goto err; 2504 goto err;
2466 } 2505 }
2467 2506
2507 if (!IS_ERR_OR_NULL(udc->transceiver))
2508 usb_register_notifier(udc->transceiver, &pxa27x_udc_phy);
2468 retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); 2509 retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
2469 if (retval) 2510 if (retval)
2470 goto err; 2511 goto err_add_gadget;
2471 2512
2472 pxa_init_debugfs(udc); 2513 pxa_init_debugfs(udc);
2473 if (should_enable_udc(udc)) 2514 if (should_enable_udc(udc))
2474 udc_enable(udc); 2515 udc_enable(udc);
2475 return 0; 2516 return 0;
2517
2518err_add_gadget:
2519 if (!IS_ERR_OR_NULL(udc->transceiver))
2520 usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
2476err: 2521err:
2477 clk_unprepare(udc->clk); 2522 clk_unprepare(udc->clk);
2478 return retval; 2523 return retval;
@@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev)
2489 usb_del_gadget_udc(&udc->gadget); 2534 usb_del_gadget_udc(&udc->gadget);
2490 pxa_cleanup_debugfs(udc); 2535 pxa_cleanup_debugfs(udc);
2491 2536
2537 if (!IS_ERR_OR_NULL(udc->transceiver))
2538 usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
2492 usb_put_phy(udc->transceiver); 2539 usb_put_phy(udc->transceiver);
2493 2540
2494 udc->transceiver = NULL; 2541 udc->transceiver = NULL;