USB: gadget: pxa25x: basic transceiver support
authorPhilipp Zabel <philipp.zabel@gmail.com>
Wed, 1 Jul 2009 10:46:25 +0000 (03:46 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Sep 2009 13:46:23 +0000 (06:46 -0700)
This adds very basic otg_transceiver support, with vbus_session
and vbus_draw callbacks.

Now VBUS sensing can be handled by an external driver which registers
the otg_transceiver interface. It also allows gadget drivers to configure
the current drawn from VBUS. The UDC driver just passes their requests
along to the transceiver driver.

Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/pxa25x_udc.c
drivers/usb/gadget/pxa25x_udc.h

index ed21e263f832f05d977aa4c4acd41881dd013002..e6fedbd5a6545d539329983ec4c2a0e666cfd118 100644 (file)
@@ -56,6 +56,7 @@
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
 
 /*
  * This driver is PXA25x only.  Grab the right register definitions.
@@ -1008,15 +1009,27 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
        return 0;
 }
 
+/* boards may consume current from VBUS, up to 100-500mA based on config.
+ * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs
+ * violate USB specs.
+ */
+static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+       struct pxa25x_udc       *udc;
+
+       udc = container_of(_gadget, struct pxa25x_udc, gadget);
+
+       if (udc->transceiver)
+               return otg_set_power(udc->transceiver, mA);
+       return -EOPNOTSUPP;
+}
+
 static const struct usb_gadget_ops pxa25x_udc_ops = {
        .get_frame      = pxa25x_udc_get_frame,
        .wakeup         = pxa25x_udc_wakeup,
        .vbus_session   = pxa25x_udc_vbus_session,
        .pullup         = pxa25x_udc_pullup,
-
-       // .vbus_draw ... boards may consume current from VBUS, up to
-       // 100-500mA based on config.  the 500uA suspend ceiling means
-       // that exclusively vbus-powered PXA designs violate USB specs.
+       .vbus_draw      = pxa25x_udc_vbus_draw,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -1303,9 +1316,23 @@ fail:
         * for set_configuration as well as eventual disconnect.
         */
        DMSG("registered gadget driver '%s'\n", driver->driver.name);
+
+       /* connect to bus through transceiver */
+       if (dev->transceiver) {
+               retval = otg_set_peripheral(dev->transceiver, &dev->gadget);
+               if (retval) {
+                       DMSG("can't bind to transceiver\n");
+                       if (driver->unbind)
+                               driver->unbind(&dev->gadget);
+                       goto bind_fail;
+               }
+       }
+
        pullup(dev);
        dump_state(dev);
        return 0;
+bind_fail:
+       return retval;
 }
 EXPORT_SYMBOL(usb_gadget_register_driver);
 
@@ -1351,6 +1378,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
        stop_activity(dev, driver);
        local_irq_enable();
 
+       if (dev->transceiver)
+               (void) otg_set_peripheral(dev->transceiver, NULL);
+
        driver->unbind(&dev->gadget);
        dev->gadget.dev.driver = NULL;
        dev->driver = NULL;
@@ -2162,6 +2192,8 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
        dev->dev = &pdev->dev;
        dev->mach = pdev->dev.platform_data;
 
+       dev->transceiver = otg_get_transceiver();
+
        if (gpio_is_valid(dev->mach->gpio_vbus)) {
                if ((retval = gpio_request(dev->mach->gpio_vbus,
                                "pxa25x_udc GPIO VBUS"))) {
@@ -2264,6 +2296,10 @@ lubbock_fail0:
        if (gpio_is_valid(dev->mach->gpio_vbus))
                gpio_free(dev->mach->gpio_vbus);
  err_gpio_vbus:
+       if (dev->transceiver) {
+               otg_put_transceiver(dev->transceiver);
+               dev->transceiver = NULL;
+       }
        clk_put(dev->clk);
  err_clk:
        return retval;
@@ -2305,6 +2341,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
 
        clk_put(dev->clk);
 
+       if (dev->transceiver) {
+               otg_put_transceiver(dev->transceiver);
+               dev->transceiver = NULL;
+       }
+
        platform_set_drvdata(pdev, NULL);
        the_controller = NULL;
        return 0;
index 1d51aa21e6eb0d6f16e5d49c0dd8a8968b60a780..f572c5617462f4c15130d53ef33b1a628064dad0 100644 (file)
@@ -128,6 +128,7 @@ struct pxa25x_udc {
        struct device                           *dev;
        struct clk                              *clk;
        struct pxa2xx_udc_mach_info             *mach;
+       struct otg_transceiver                  *transceiver;
        u64                                     dma_mask;
        struct pxa25x_ep                        ep [PXA_UDC_NUM_ENDPOINTS];