diff options
author | Philipp Zabel <philipp.zabel@gmail.com> | 2009-07-01 14:46:25 +0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 17:46:23 +0400 |
commit | ab26d20f3ef1105d889f702cd01fba8c6fb32f73 (patch) | |
tree | afe07b1938e05f83b5824feaa3a4d8e6d8bdce64 /drivers/usb/gadget/pxa25x_udc.c | |
parent | 4c6e8971cbe0148085fcf6fd30eaa3c39f8a8cce (diff) | |
download | linux-ab26d20f3ef1105d889f702cd01fba8c6fb32f73.tar.xz |
USB: gadget: pxa25x: basic transceiver support
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>
Diffstat (limited to 'drivers/usb/gadget/pxa25x_udc.c')
-rw-r--r-- | drivers/usb/gadget/pxa25x_udc.c | 49 |
1 files changed, 45 insertions, 4 deletions
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index ed21e263f832..e6fedbd5a654 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -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; |