Commit ab26d20f authored by Philipp Zabel's avatar Philipp Zabel Committed by Greg Kroah-Hartman

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: default avatarPhilipp Zabel <philipp.zabel@gmail.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4c6e8971
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
/* /*
* This driver is PXA25x only. Grab the right register definitions. * 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) ...@@ -1008,15 +1009,27 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
return 0; 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 = { static const struct usb_gadget_ops pxa25x_udc_ops = {
.get_frame = pxa25x_udc_get_frame, .get_frame = pxa25x_udc_get_frame,
.wakeup = pxa25x_udc_wakeup, .wakeup = pxa25x_udc_wakeup,
.vbus_session = pxa25x_udc_vbus_session, .vbus_session = pxa25x_udc_vbus_session,
.pullup = pxa25x_udc_pullup, .pullup = pxa25x_udc_pullup,
.vbus_draw = pxa25x_udc_vbus_draw,
// .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.
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -1303,9 +1316,23 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) ...@@ -1303,9 +1316,23 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
* for set_configuration as well as eventual disconnect. * for set_configuration as well as eventual disconnect.
*/ */
DMSG("registered gadget driver '%s'\n", driver->driver.name); 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); pullup(dev);
dump_state(dev); dump_state(dev);
return 0; return 0;
bind_fail:
return retval;
} }
EXPORT_SYMBOL(usb_gadget_register_driver); EXPORT_SYMBOL(usb_gadget_register_driver);
...@@ -1351,6 +1378,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ...@@ -1351,6 +1378,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
stop_activity(dev, driver); stop_activity(dev, driver);
local_irq_enable(); local_irq_enable();
if (dev->transceiver)
(void) otg_set_peripheral(dev->transceiver, NULL);
driver->unbind(&dev->gadget); driver->unbind(&dev->gadget);
dev->gadget.dev.driver = NULL; dev->gadget.dev.driver = NULL;
dev->driver = NULL; dev->driver = NULL;
...@@ -2162,6 +2192,8 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) ...@@ -2162,6 +2192,8 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
dev->dev = &pdev->dev; dev->dev = &pdev->dev;
dev->mach = pdev->dev.platform_data; dev->mach = pdev->dev.platform_data;
dev->transceiver = otg_get_transceiver();
if (gpio_is_valid(dev->mach->gpio_vbus)) { if (gpio_is_valid(dev->mach->gpio_vbus)) {
if ((retval = gpio_request(dev->mach->gpio_vbus, if ((retval = gpio_request(dev->mach->gpio_vbus,
"pxa25x_udc GPIO VBUS"))) { "pxa25x_udc GPIO VBUS"))) {
...@@ -2264,6 +2296,10 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) ...@@ -2264,6 +2296,10 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
if (gpio_is_valid(dev->mach->gpio_vbus)) if (gpio_is_valid(dev->mach->gpio_vbus))
gpio_free(dev->mach->gpio_vbus); gpio_free(dev->mach->gpio_vbus);
err_gpio_vbus: err_gpio_vbus:
if (dev->transceiver) {
otg_put_transceiver(dev->transceiver);
dev->transceiver = NULL;
}
clk_put(dev->clk); clk_put(dev->clk);
err_clk: err_clk:
return retval; return retval;
...@@ -2305,6 +2341,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) ...@@ -2305,6 +2341,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
clk_put(dev->clk); clk_put(dev->clk);
if (dev->transceiver) {
otg_put_transceiver(dev->transceiver);
dev->transceiver = NULL;
}
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
the_controller = NULL; the_controller = NULL;
return 0; return 0;
......
...@@ -128,6 +128,7 @@ struct pxa25x_udc { ...@@ -128,6 +128,7 @@ struct pxa25x_udc {
struct device *dev; struct device *dev;
struct clk *clk; struct clk *clk;
struct pxa2xx_udc_mach_info *mach; struct pxa2xx_udc_mach_info *mach;
struct otg_transceiver *transceiver;
u64 dma_mask; u64 dma_mask;
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment