diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2009-01-25 02:55:34 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-03-24 19:20:26 -0400 |
commit | eb50702539f9be3765127d927d3e9ccfeb65f26e (patch) | |
tree | dcb3885aa1c037c2a2113d2451a28bd5a5c18537 /drivers/usb/gadget | |
parent | c2344f13b59e007d782a3e591ebc551bc583a8b7 (diff) |
USB: pxa27x_udc: factor pullup code to prepare otg transceiver
Prepare pxa27x_udc to handle usb D+ pullup properly : it
should connect the pullup resistor and disconnect it only
if no external transceiver is handling it.
[ dbrownell@users.sourceforge.net: kerneldoc and gpio fixes ]
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/pxa27x_udc.c | 140 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa27x_udc.h | 6 |
2 files changed, 138 insertions, 8 deletions
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 990f40f988d4..f8145590944f 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/proc_fs.h> | 30 | #include <linux/proc_fs.h> |
31 | #include <linux/clk.h> | 31 | #include <linux/clk.h> |
32 | #include <linux/irq.h> | 32 | #include <linux/irq.h> |
33 | #include <linux/gpio.h> | ||
33 | 34 | ||
34 | #include <asm/byteorder.h> | 35 | #include <asm/byteorder.h> |
35 | #include <mach/hardware.h> | 36 | #include <mach/hardware.h> |
@@ -1471,6 +1472,32 @@ static struct usb_ep_ops pxa_ep_ops = { | |||
1471 | .fifo_flush = pxa_ep_fifo_flush, | 1472 | .fifo_flush = pxa_ep_fifo_flush, |
1472 | }; | 1473 | }; |
1473 | 1474 | ||
1475 | /** | ||
1476 | * dplus_pullup - Connect or disconnect pullup resistor to D+ pin | ||
1477 | * @udc: udc device | ||
1478 | * @on: 0 if disconnect pullup resistor, 1 otherwise | ||
1479 | * Context: any | ||
1480 | * | ||
1481 | * Handle D+ pullup resistor, make the device visible to the usb bus, and | ||
1482 | * declare it as a full speed usb device | ||
1483 | */ | ||
1484 | static void dplus_pullup(struct pxa_udc *udc, int on) | ||
1485 | { | ||
1486 | if (on) { | ||
1487 | if (gpio_is_valid(udc->mach->gpio_pullup)) | ||
1488 | gpio_set_value(udc->mach->gpio_pullup, | ||
1489 | !udc->mach->gpio_pullup_inverted); | ||
1490 | if (udc->mach->udc_command) | ||
1491 | udc->mach->udc_command(PXA2XX_UDC_CMD_CONNECT); | ||
1492 | } else { | ||
1493 | if (gpio_is_valid(udc->mach->gpio_pullup)) | ||
1494 | gpio_set_value(udc->mach->gpio_pullup, | ||
1495 | udc->mach->gpio_pullup_inverted); | ||
1496 | if (udc->mach->udc_command) | ||
1497 | udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); | ||
1498 | } | ||
1499 | udc->pullup_on = on; | ||
1500 | } | ||
1474 | 1501 | ||
1475 | /** | 1502 | /** |
1476 | * pxa_udc_get_frame - Returns usb frame number | 1503 | * pxa_udc_get_frame - Returns usb frame number |
@@ -1500,21 +1527,91 @@ static int pxa_udc_wakeup(struct usb_gadget *_gadget) | |||
1500 | return 0; | 1527 | return 0; |
1501 | } | 1528 | } |
1502 | 1529 | ||
1530 | static void udc_enable(struct pxa_udc *udc); | ||
1531 | static void udc_disable(struct pxa_udc *udc); | ||
1532 | |||
1533 | /** | ||
1534 | * should_enable_udc - Tells if UDC should be enabled | ||
1535 | * @udc: udc device | ||
1536 | * Context: any | ||
1537 | * | ||
1538 | * The UDC should be enabled if : | ||
1539 | * - the pullup resistor is connected | ||
1540 | * - and a gadget driver is bound | ||
1541 | * | ||
1542 | * Returns 1 if UDC should be enabled, 0 otherwise | ||
1543 | */ | ||
1544 | static int should_enable_udc(struct pxa_udc *udc) | ||
1545 | { | ||
1546 | int put_on; | ||
1547 | |||
1548 | put_on = ((udc->pullup_on) && (udc->driver)); | ||
1549 | return put_on; | ||
1550 | } | ||
1551 | |||
1552 | /** | ||
1553 | * should_disable_udc - Tells if UDC should be disabled | ||
1554 | * @udc: udc device | ||
1555 | * Context: any | ||
1556 | * | ||
1557 | * The UDC should be disabled if : | ||
1558 | * - the pullup resistor is not connected | ||
1559 | * - or no gadget driver is bound | ||
1560 | * | ||
1561 | * Returns 1 if UDC should be disabled | ||
1562 | */ | ||
1563 | static int should_disable_udc(struct pxa_udc *udc) | ||
1564 | { | ||
1565 | int put_off; | ||
1566 | |||
1567 | put_off = ((!udc->pullup_on) || (!udc->driver)); | ||
1568 | return put_off; | ||
1569 | } | ||
1570 | |||
1571 | /** | ||
1572 | * pxa_udc_pullup - Offer manual D+ pullup control | ||
1573 | * @_gadget: usb gadget using the control | ||
1574 | * @is_active: 0 if disconnect, else connect D+ pullup resistor | ||
1575 | * Context: !in_interrupt() | ||
1576 | * | ||
1577 | * Returns 0 if OK, -EOPNOTSUPP if udc driver doesn't handle D+ pullup | ||
1578 | */ | ||
1579 | static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) | ||
1580 | { | ||
1581 | struct pxa_udc *udc = to_gadget_udc(_gadget); | ||
1582 | |||
1583 | if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) | ||
1584 | return -EOPNOTSUPP; | ||
1585 | |||
1586 | dplus_pullup(udc, is_active); | ||
1587 | |||
1588 | if (should_enable_udc(udc)) | ||
1589 | udc_enable(udc); | ||
1590 | if (should_disable_udc(udc)) | ||
1591 | udc_disable(udc); | ||
1592 | return 0; | ||
1593 | } | ||
1594 | |||
1503 | static const struct usb_gadget_ops pxa_udc_ops = { | 1595 | static const struct usb_gadget_ops pxa_udc_ops = { |
1504 | .get_frame = pxa_udc_get_frame, | 1596 | .get_frame = pxa_udc_get_frame, |
1505 | .wakeup = pxa_udc_wakeup, | 1597 | .wakeup = pxa_udc_wakeup, |
1598 | .pullup = pxa_udc_pullup, | ||
1506 | /* current versions must always be self-powered */ | 1599 | /* current versions must always be self-powered */ |
1507 | }; | 1600 | }; |
1508 | 1601 | ||
1509 | /** | 1602 | /** |
1510 | * udc_disable - disable udc device controller | 1603 | * udc_disable - disable udc device controller |
1511 | * @udc: udc device | 1604 | * @udc: udc device |
1605 | * Context: any | ||
1512 | * | 1606 | * |
1513 | * Disables the udc device : disables clocks, udc interrupts, control endpoint | 1607 | * Disables the udc device : disables clocks, udc interrupts, control endpoint |
1514 | * interrupts. | 1608 | * interrupts. |
1515 | */ | 1609 | */ |
1516 | static void udc_disable(struct pxa_udc *udc) | 1610 | static void udc_disable(struct pxa_udc *udc) |
1517 | { | 1611 | { |
1612 | if (!udc->enabled) | ||
1613 | return; | ||
1614 | |||
1518 | udc_writel(udc, UDCICR0, 0); | 1615 | udc_writel(udc, UDCICR0, 0); |
1519 | udc_writel(udc, UDCICR1, 0); | 1616 | udc_writel(udc, UDCICR1, 0); |
1520 | 1617 | ||
@@ -1523,8 +1620,8 @@ static void udc_disable(struct pxa_udc *udc) | |||
1523 | 1620 | ||
1524 | ep0_idle(udc); | 1621 | ep0_idle(udc); |
1525 | udc->gadget.speed = USB_SPEED_UNKNOWN; | 1622 | udc->gadget.speed = USB_SPEED_UNKNOWN; |
1526 | if (udc->mach->udc_command) | 1623 | |
1527 | udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); | 1624 | udc->enabled = 0; |
1528 | } | 1625 | } |
1529 | 1626 | ||
1530 | /** | 1627 | /** |
@@ -1570,6 +1667,9 @@ static __init void udc_init_data(struct pxa_udc *dev) | |||
1570 | */ | 1667 | */ |
1571 | static void udc_enable(struct pxa_udc *udc) | 1668 | static void udc_enable(struct pxa_udc *udc) |
1572 | { | 1669 | { |
1670 | if (udc->enabled) | ||
1671 | return; | ||
1672 | |||
1573 | udc_writel(udc, UDCICR0, 0); | 1673 | udc_writel(udc, UDCICR0, 0); |
1574 | udc_writel(udc, UDCICR1, 0); | 1674 | udc_writel(udc, UDCICR1, 0); |
1575 | udc_clear_mask_UDCCR(udc, UDCCR_UDE); | 1675 | udc_clear_mask_UDCCR(udc, UDCCR_UDE); |
@@ -1598,9 +1698,7 @@ static void udc_enable(struct pxa_udc *udc) | |||
1598 | /* enable ep0 irqs */ | 1698 | /* enable ep0 irqs */ |
1599 | pio_irq_enable(&udc->pxa_ep[0]); | 1699 | pio_irq_enable(&udc->pxa_ep[0]); |
1600 | 1700 | ||
1601 | dev_info(udc->dev, "UDC connecting\n"); | 1701 | udc->enabled = 1; |
1602 | if (udc->mach->udc_command) | ||
1603 | udc->mach->udc_command(PXA2XX_UDC_CMD_CONNECT); | ||
1604 | } | 1702 | } |
1605 | 1703 | ||
1606 | /** | 1704 | /** |
@@ -1612,6 +1710,9 @@ static void udc_enable(struct pxa_udc *udc) | |||
1612 | * usb traffic follows until a disconnect is reported. Then a host may connect | 1710 | * usb traffic follows until a disconnect is reported. Then a host may connect |
1613 | * again, or the driver might get unbound. | 1711 | * again, or the driver might get unbound. |
1614 | * | 1712 | * |
1713 | * Note that the udc is not automatically enabled. Check function | ||
1714 | * should_enable_udc(). | ||
1715 | * | ||
1615 | * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise | 1716 | * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise |
1616 | */ | 1717 | */ |
1617 | int usb_gadget_register_driver(struct usb_gadget_driver *driver) | 1718 | int usb_gadget_register_driver(struct usb_gadget_driver *driver) |
@@ -1630,6 +1731,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) | |||
1630 | /* first hook up the driver ... */ | 1731 | /* first hook up the driver ... */ |
1631 | udc->driver = driver; | 1732 | udc->driver = driver; |
1632 | udc->gadget.dev.driver = &driver->driver; | 1733 | udc->gadget.dev.driver = &driver->driver; |
1734 | dplus_pullup(udc, 1); | ||
1633 | 1735 | ||
1634 | retval = device_add(&udc->gadget.dev); | 1736 | retval = device_add(&udc->gadget.dev); |
1635 | if (retval) { | 1737 | if (retval) { |
@@ -1645,7 +1747,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) | |||
1645 | dev_dbg(udc->dev, "registered gadget driver '%s'\n", | 1747 | dev_dbg(udc->dev, "registered gadget driver '%s'\n", |
1646 | driver->driver.name); | 1748 | driver->driver.name); |
1647 | 1749 | ||
1648 | udc_enable(udc); | 1750 | if (should_enable_udc(udc)) |
1751 | udc_enable(udc); | ||
1649 | return 0; | 1752 | return 0; |
1650 | 1753 | ||
1651 | bind_fail: | 1754 | bind_fail: |
@@ -1699,6 +1802,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) | |||
1699 | 1802 | ||
1700 | stop_activity(udc, driver); | 1803 | stop_activity(udc, driver); |
1701 | udc_disable(udc); | 1804 | udc_disable(udc); |
1805 | dplus_pullup(udc, 0); | ||
1702 | 1806 | ||
1703 | driver->unbind(&udc->gadget); | 1807 | driver->unbind(&udc->gadget); |
1704 | udc->driver = NULL; | 1808 | udc->driver = NULL; |
@@ -2212,7 +2316,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) | |||
2212 | { | 2316 | { |
2213 | struct resource *regs; | 2317 | struct resource *regs; |
2214 | struct pxa_udc *udc = &memory; | 2318 | struct pxa_udc *udc = &memory; |
2215 | int retval; | 2319 | int retval = 0, gpio; |
2216 | 2320 | ||
2217 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 2321 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
2218 | if (!regs) | 2322 | if (!regs) |
@@ -2224,6 +2328,19 @@ static int __init pxa_udc_probe(struct platform_device *pdev) | |||
2224 | udc->dev = &pdev->dev; | 2328 | udc->dev = &pdev->dev; |
2225 | udc->mach = pdev->dev.platform_data; | 2329 | udc->mach = pdev->dev.platform_data; |
2226 | 2330 | ||
2331 | gpio = udc->mach->gpio_pullup; | ||
2332 | if (gpio_is_valid(gpio)) { | ||
2333 | retval = gpio_request(gpio, "USB D+ pullup"); | ||
2334 | if (retval == 0) | ||
2335 | gpio_direction_output(gpio, | ||
2336 | udc->mach->gpio_pullup_inverted); | ||
2337 | } | ||
2338 | if (retval) { | ||
2339 | dev_err(&pdev->dev, "Couldn't request gpio %d : %d\n", | ||
2340 | gpio, retval); | ||
2341 | return retval; | ||
2342 | } | ||
2343 | |||
2227 | udc->clk = clk_get(&pdev->dev, NULL); | 2344 | udc->clk = clk_get(&pdev->dev, NULL); |
2228 | if (IS_ERR(udc->clk)) { | 2345 | if (IS_ERR(udc->clk)) { |
2229 | retval = PTR_ERR(udc->clk); | 2346 | retval = PTR_ERR(udc->clk); |
@@ -2273,10 +2390,13 @@ err_clk: | |||
2273 | static int __exit pxa_udc_remove(struct platform_device *_dev) | 2390 | static int __exit pxa_udc_remove(struct platform_device *_dev) |
2274 | { | 2391 | { |
2275 | struct pxa_udc *udc = platform_get_drvdata(_dev); | 2392 | struct pxa_udc *udc = platform_get_drvdata(_dev); |
2393 | int gpio = udc->mach->gpio_pullup; | ||
2276 | 2394 | ||
2277 | usb_gadget_unregister_driver(udc->driver); | 2395 | usb_gadget_unregister_driver(udc->driver); |
2278 | free_irq(udc->irq, udc); | 2396 | free_irq(udc->irq, udc); |
2279 | pxa_cleanup_debugfs(udc); | 2397 | pxa_cleanup_debugfs(udc); |
2398 | if (gpio_is_valid(gpio)) | ||
2399 | gpio_free(gpio); | ||
2280 | 2400 | ||
2281 | platform_set_drvdata(_dev, NULL); | 2401 | platform_set_drvdata(_dev, NULL); |
2282 | the_controller = NULL; | 2402 | the_controller = NULL; |
@@ -2319,6 +2439,8 @@ static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state) | |||
2319 | } | 2439 | } |
2320 | 2440 | ||
2321 | udc_disable(udc); | 2441 | udc_disable(udc); |
2442 | udc->pullup_resume = udc->pullup_on; | ||
2443 | dplus_pullup(udc, 0); | ||
2322 | 2444 | ||
2323 | return 0; | 2445 | return 0; |
2324 | } | 2446 | } |
@@ -2346,7 +2468,9 @@ static int pxa_udc_resume(struct platform_device *_dev) | |||
2346 | ep->udccsr_value, ep->udccr_value); | 2468 | ep->udccsr_value, ep->udccr_value); |
2347 | } | 2469 | } |
2348 | 2470 | ||
2349 | udc_enable(udc); | 2471 | dplus_pullup(udc, udc->pullup_resume); |
2472 | if (should_enable_udc(udc)) | ||
2473 | udc_enable(udc); | ||
2350 | /* | 2474 | /* |
2351 | * We do not handle OTG yet. | 2475 | * We do not handle OTG yet. |
2352 | * | 2476 | * |
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index 1d1b7936ee11..6f5234dff8a5 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h | |||
@@ -425,6 +425,9 @@ struct udc_stats { | |||
425 | * @stats: statistics on udc usage | 425 | * @stats: statistics on udc usage |
426 | * @udc_usb_ep: array of usb endpoints offered by the gadget | 426 | * @udc_usb_ep: array of usb endpoints offered by the gadget |
427 | * @pxa_ep: array of pxa available endpoints | 427 | * @pxa_ep: array of pxa available endpoints |
428 | * @enabled: UDC was enabled by a previous udc_enable() | ||
429 | * @pullup_on: if pullup resistor connected to D+ pin | ||
430 | * @pullup_resume: if pullup resistor should be connected to D+ pin on resume | ||
428 | * @config: UDC active configuration | 431 | * @config: UDC active configuration |
429 | * @last_interface: UDC interface of the last SET_INTERFACE host request | 432 | * @last_interface: UDC interface of the last SET_INTERFACE host request |
430 | * @last_alternate: UDC altsetting of the last SET_INTERFACE host request | 433 | * @last_alternate: UDC altsetting of the last SET_INTERFACE host request |
@@ -450,6 +453,9 @@ struct pxa_udc { | |||
450 | struct udc_usb_ep udc_usb_ep[NR_USB_ENDPOINTS]; | 453 | struct udc_usb_ep udc_usb_ep[NR_USB_ENDPOINTS]; |
451 | struct pxa_ep pxa_ep[NR_PXA_ENDPOINTS]; | 454 | struct pxa_ep pxa_ep[NR_PXA_ENDPOINTS]; |
452 | 455 | ||
456 | unsigned enabled:1; | ||
457 | unsigned pullup_on:1; | ||
458 | unsigned pullup_resume:1; | ||
453 | unsigned config:2; | 459 | unsigned config:2; |
454 | unsigned last_interface:3; | 460 | unsigned last_interface:3; |
455 | unsigned last_alternate:3; | 461 | unsigned last_alternate:3; |