diff options
Diffstat (limited to 'drivers/usb/host/sl811-hcd.c')
-rw-r--r-- | drivers/usb/host/sl811-hcd.c | 146 |
1 files changed, 81 insertions, 65 deletions
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index a374b7692073..99d43f758ad0 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c | |||
@@ -2,8 +2,8 @@ | |||
2 | * SL811HS HCD (Host Controller Driver) for USB. | 2 | * SL811HS HCD (Host Controller Driver) for USB. |
3 | * | 3 | * |
4 | * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) | 4 | * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) |
5 | * Copyright (C) 2004 David Brownell | 5 | * Copyright (C) 2004-2005 David Brownell |
6 | * | 6 | * |
7 | * Periodic scheduling is based on Roman's OHCI code | 7 | * Periodic scheduling is based on Roman's OHCI code |
8 | * Copyright (C) 1999 Roman Weissgaerber | 8 | * Copyright (C) 1999 Roman Weissgaerber |
9 | * | 9 | * |
@@ -15,7 +15,7 @@ | |||
15 | * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" | 15 | * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" |
16 | * document (providing significant pieces missing from that spec); plus | 16 | * document (providing significant pieces missing from that spec); plus |
17 | * the SL811S spec if you want peripheral side info. | 17 | * the SL811S spec if you want peripheral side info. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | /* | 20 | /* |
21 | * Status: Passed basic stress testing, works with hubs, mice, keyboards, | 21 | * Status: Passed basic stress testing, works with hubs, mice, keyboards, |
@@ -67,7 +67,7 @@ | |||
67 | MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); | 67 | MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); |
68 | MODULE_LICENSE("GPL"); | 68 | MODULE_LICENSE("GPL"); |
69 | 69 | ||
70 | #define DRIVER_VERSION "15 Dec 2004" | 70 | #define DRIVER_VERSION "19 May 2005" |
71 | 71 | ||
72 | 72 | ||
73 | #ifndef DEBUG | 73 | #ifndef DEBUG |
@@ -121,6 +121,10 @@ static void port_power(struct sl811 *sl811, int is_on) | |||
121 | /* reset as thoroughly as we can */ | 121 | /* reset as thoroughly as we can */ |
122 | if (sl811->board && sl811->board->reset) | 122 | if (sl811->board && sl811->board->reset) |
123 | sl811->board->reset(hcd->self.controller); | 123 | sl811->board->reset(hcd->self.controller); |
124 | else { | ||
125 | sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0); | ||
126 | mdelay(20); | ||
127 | } | ||
124 | 128 | ||
125 | sl811_write(sl811, SL11H_IRQ_ENABLE, 0); | 129 | sl811_write(sl811, SL11H_IRQ_ENABLE, 0); |
126 | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 130 | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); |
@@ -443,6 +447,7 @@ static void finish_request( | |||
443 | spin_lock(&urb->lock); | 447 | spin_lock(&urb->lock); |
444 | if (urb->status == -EINPROGRESS) | 448 | if (urb->status == -EINPROGRESS) |
445 | urb->status = status; | 449 | urb->status = status; |
450 | urb->hcpriv = NULL; | ||
446 | spin_unlock(&urb->lock); | 451 | spin_unlock(&urb->lock); |
447 | 452 | ||
448 | spin_unlock(&sl811->lock); | 453 | spin_unlock(&sl811->lock); |
@@ -472,7 +477,7 @@ static void finish_request( | |||
472 | if (*prev) | 477 | if (*prev) |
473 | *prev = ep->next; | 478 | *prev = ep->next; |
474 | sl811->load[i] -= ep->load; | 479 | sl811->load[i] -= ep->load; |
475 | } | 480 | } |
476 | ep->branch = PERIODIC_SIZE; | 481 | ep->branch = PERIODIC_SIZE; |
477 | sl811->periodic_count--; | 482 | sl811->periodic_count--; |
478 | sl811_to_hcd(sl811)->self.bandwidth_allocated | 483 | sl811_to_hcd(sl811)->self.bandwidth_allocated |
@@ -661,9 +666,9 @@ retry: | |||
661 | 666 | ||
662 | #ifdef QUIRK2 | 667 | #ifdef QUIRK2 |
663 | /* this may no longer be necessary ... */ | 668 | /* this may no longer be necessary ... */ |
664 | if (irqstat == 0 && ret == IRQ_NONE) { | 669 | if (irqstat == 0) { |
665 | irqstat = checkdone(sl811); | 670 | irqstat = checkdone(sl811); |
666 | if (irqstat /* && irq != ~0 */ ) | 671 | if (irqstat) |
667 | sl811->stat_lost++; | 672 | sl811->stat_lost++; |
668 | } | 673 | } |
669 | #endif | 674 | #endif |
@@ -722,7 +727,8 @@ retry: | |||
722 | if (sl811->active_a) { | 727 | if (sl811->active_a) { |
723 | sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); | 728 | sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); |
724 | finish_request(sl811, sl811->active_a, | 729 | finish_request(sl811, sl811->active_a, |
725 | container_of(sl811->active_a->hep->urb_list.next, | 730 | container_of(sl811->active_a |
731 | ->hep->urb_list.next, | ||
726 | struct urb, urb_list), | 732 | struct urb, urb_list), |
727 | NULL, -ESHUTDOWN); | 733 | NULL, -ESHUTDOWN); |
728 | sl811->active_a = NULL; | 734 | sl811->active_a = NULL; |
@@ -731,7 +737,8 @@ retry: | |||
731 | if (sl811->active_b) { | 737 | if (sl811->active_b) { |
732 | sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); | 738 | sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); |
733 | finish_request(sl811, sl811->active_b, | 739 | finish_request(sl811, sl811->active_b, |
734 | container_of(sl811->active_b->hep->urb_list.next, | 740 | container_of(sl811->active_b |
741 | ->hep->urb_list.next, | ||
735 | struct urb, urb_list), | 742 | struct urb, urb_list), |
736 | NULL, -ESHUTDOWN); | 743 | NULL, -ESHUTDOWN); |
737 | sl811->active_b = NULL; | 744 | sl811->active_b = NULL; |
@@ -761,7 +768,7 @@ retry: | |||
761 | goto retry; | 768 | goto retry; |
762 | } | 769 | } |
763 | 770 | ||
764 | if (sl811->periodic_count == 0 && list_empty(&sl811->async)) | 771 | if (sl811->periodic_count == 0 && list_empty(&sl811->async)) |
765 | sofirq_off(sl811); | 772 | sofirq_off(sl811); |
766 | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); | 773 | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); |
767 | 774 | ||
@@ -796,7 +803,7 @@ static int balance(struct sl811 *sl811, u16 period, u16 load) | |||
796 | } | 803 | } |
797 | if (j < PERIODIC_SIZE) | 804 | if (j < PERIODIC_SIZE) |
798 | continue; | 805 | continue; |
799 | branch = i; | 806 | branch = i; |
800 | } | 807 | } |
801 | } | 808 | } |
802 | return branch; | 809 | return branch; |
@@ -890,6 +897,7 @@ static int sl811h_urb_enqueue( | |||
890 | break; | 897 | break; |
891 | } | 898 | } |
892 | 899 | ||
900 | ep->hep = hep; | ||
893 | hep->hcpriv = ep; | 901 | hep->hcpriv = ep; |
894 | } | 902 | } |
895 | 903 | ||
@@ -961,15 +969,16 @@ fail: | |||
961 | static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) | 969 | static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) |
962 | { | 970 | { |
963 | struct sl811 *sl811 = hcd_to_sl811(hcd); | 971 | struct sl811 *sl811 = hcd_to_sl811(hcd); |
964 | struct usb_host_endpoint *hep = urb->hcpriv; | 972 | struct usb_host_endpoint *hep; |
965 | unsigned long flags; | 973 | unsigned long flags; |
966 | struct sl811h_ep *ep; | 974 | struct sl811h_ep *ep; |
967 | int retval = 0; | 975 | int retval = 0; |
968 | 976 | ||
977 | spin_lock_irqsave(&sl811->lock, flags); | ||
978 | hep = urb->hcpriv; | ||
969 | if (!hep) | 979 | if (!hep) |
970 | return -EINVAL; | 980 | goto fail; |
971 | 981 | ||
972 | spin_lock_irqsave(&sl811->lock, flags); | ||
973 | ep = hep->hcpriv; | 982 | ep = hep->hcpriv; |
974 | if (ep) { | 983 | if (ep) { |
975 | /* finish right away if this urb can't be active ... | 984 | /* finish right away if this urb can't be active ... |
@@ -1017,6 +1026,7 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) | |||
1017 | VDBG("dequeue, urb %p active %s; wait4irq\n", urb, | 1026 | VDBG("dequeue, urb %p active %s; wait4irq\n", urb, |
1018 | (sl811->active_a == ep) ? "A" : "B"); | 1027 | (sl811->active_a == ep) ? "A" : "B"); |
1019 | } else | 1028 | } else |
1029 | fail: | ||
1020 | retval = -EINVAL; | 1030 | retval = -EINVAL; |
1021 | spin_unlock_irqrestore(&sl811->lock, flags); | 1031 | spin_unlock_irqrestore(&sl811->lock, flags); |
1022 | return retval; | 1032 | return retval; |
@@ -1576,6 +1586,9 @@ sl811h_start(struct usb_hcd *hcd) | |||
1576 | if (sl811->board && sl811->board->power) | 1586 | if (sl811->board && sl811->board->power) |
1577 | hub_set_power_budget(udev, sl811->board->power * 2); | 1587 | hub_set_power_budget(udev, sl811->board->power * 2); |
1578 | 1588 | ||
1589 | /* enable power and interupts */ | ||
1590 | port_power(sl811, 1); | ||
1591 | |||
1579 | return 0; | 1592 | return 0; |
1580 | } | 1593 | } |
1581 | 1594 | ||
@@ -1618,7 +1631,7 @@ static struct hc_driver sl811h_hc_driver = { | |||
1618 | 1631 | ||
1619 | /*-------------------------------------------------------------------------*/ | 1632 | /*-------------------------------------------------------------------------*/ |
1620 | 1633 | ||
1621 | static int __init_or_module | 1634 | static int __devexit |
1622 | sl811h_remove(struct device *dev) | 1635 | sl811h_remove(struct device *dev) |
1623 | { | 1636 | { |
1624 | struct usb_hcd *hcd = dev_get_drvdata(dev); | 1637 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
@@ -1631,21 +1644,20 @@ sl811h_remove(struct device *dev) | |||
1631 | remove_debug_file(sl811); | 1644 | remove_debug_file(sl811); |
1632 | usb_remove_hcd(hcd); | 1645 | usb_remove_hcd(hcd); |
1633 | 1646 | ||
1634 | iounmap(sl811->data_reg); | 1647 | /* some platforms may use IORESOURCE_IO */ |
1635 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 1648 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
1636 | release_mem_region(res->start, 1); | 1649 | if (res) |
1650 | iounmap(sl811->data_reg); | ||
1637 | 1651 | ||
1638 | iounmap(sl811->addr_reg); | ||
1639 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1652 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1640 | release_mem_region(res->start, 1); | 1653 | if (res) |
1654 | iounmap(sl811->addr_reg); | ||
1641 | 1655 | ||
1642 | usb_put_hcd(hcd); | 1656 | usb_put_hcd(hcd); |
1643 | return 0; | 1657 | return 0; |
1644 | } | 1658 | } |
1645 | 1659 | ||
1646 | #define resource_len(r) (((r)->end - (r)->start) + 1) | 1660 | static int __devinit |
1647 | |||
1648 | static int __init | ||
1649 | sl811h_probe(struct device *dev) | 1661 | sl811h_probe(struct device *dev) |
1650 | { | 1662 | { |
1651 | struct usb_hcd *hcd; | 1663 | struct usb_hcd *hcd; |
@@ -1656,7 +1668,7 @@ sl811h_probe(struct device *dev) | |||
1656 | void __iomem *addr_reg; | 1668 | void __iomem *addr_reg; |
1657 | void __iomem *data_reg; | 1669 | void __iomem *data_reg; |
1658 | int retval; | 1670 | int retval; |
1659 | u8 tmp; | 1671 | u8 tmp, ioaddr = 0; |
1660 | 1672 | ||
1661 | /* basic sanity checks first. board-specific init logic should | 1673 | /* basic sanity checks first. board-specific init logic should |
1662 | * have initialized these three resources and probably board | 1674 | * have initialized these three resources and probably board |
@@ -1664,13 +1676,8 @@ sl811h_probe(struct device *dev) | |||
1664 | * minimal sanity checking. | 1676 | * minimal sanity checking. |
1665 | */ | 1677 | */ |
1666 | pdev = container_of(dev, struct platform_device, dev); | 1678 | pdev = container_of(dev, struct platform_device, dev); |
1667 | if (pdev->num_resources < 3) | ||
1668 | return -ENODEV; | ||
1669 | |||
1670 | addr = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1671 | data = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
1672 | irq = platform_get_irq(pdev, 0); | 1679 | irq = platform_get_irq(pdev, 0); |
1673 | if (!addr || !data || irq < 0) | 1680 | if (pdev->num_resources < 3 || irq < 0) |
1674 | return -ENODEV; | 1681 | return -ENODEV; |
1675 | 1682 | ||
1676 | /* refuse to confuse usbcore */ | 1683 | /* refuse to confuse usbcore */ |
@@ -1679,24 +1686,31 @@ sl811h_probe(struct device *dev) | |||
1679 | return -EINVAL; | 1686 | return -EINVAL; |
1680 | } | 1687 | } |
1681 | 1688 | ||
1682 | if (!request_mem_region(addr->start, 1, hcd_name)) { | 1689 | /* the chip may be wired for either kind of addressing */ |
1683 | retval = -EBUSY; | 1690 | addr = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1684 | goto err1; | 1691 | data = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
1685 | } | 1692 | retval = -EBUSY; |
1686 | addr_reg = ioremap(addr->start, resource_len(addr)); | 1693 | if (!addr || !data) { |
1687 | if (addr_reg == NULL) { | 1694 | addr = platform_get_resource(pdev, IORESOURCE_IO, 0); |
1688 | retval = -ENOMEM; | 1695 | data = platform_get_resource(pdev, IORESOURCE_IO, 1); |
1689 | goto err2; | 1696 | if (!addr || !data) |
1690 | } | 1697 | return -ENODEV; |
1698 | ioaddr = 1; | ||
1699 | |||
1700 | addr_reg = (void __iomem *) addr->start; | ||
1701 | data_reg = (void __iomem *) data->start; | ||
1702 | } else { | ||
1703 | addr_reg = ioremap(addr->start, 1); | ||
1704 | if (addr_reg == NULL) { | ||
1705 | retval = -ENOMEM; | ||
1706 | goto err2; | ||
1707 | } | ||
1691 | 1708 | ||
1692 | if (!request_mem_region(data->start, 1, hcd_name)) { | 1709 | data_reg = ioremap(data->start, 1); |
1693 | retval = -EBUSY; | 1710 | if (data_reg == NULL) { |
1694 | goto err3; | 1711 | retval = -ENOMEM; |
1695 | } | 1712 | goto err4; |
1696 | data_reg = ioremap(data->start, resource_len(addr)); | 1713 | } |
1697 | if (data_reg == NULL) { | ||
1698 | retval = -ENOMEM; | ||
1699 | goto err4; | ||
1700 | } | 1714 | } |
1701 | 1715 | ||
1702 | /* allocate and initialize hcd */ | 1716 | /* allocate and initialize hcd */ |
@@ -1737,12 +1751,14 @@ sl811h_probe(struct device *dev) | |||
1737 | goto err6; | 1751 | goto err6; |
1738 | } | 1752 | } |
1739 | 1753 | ||
1740 | /* sl811s would need a different handler for this irq */ | 1754 | /* The chip's IRQ is level triggered, active high. A requirement |
1741 | #ifdef CONFIG_ARM | 1755 | * for platform device setup is to cope with things like signal |
1742 | /* Cypress docs say the IRQ is IRQT_HIGH ... */ | 1756 | * inverters (e.g. CF is active low) or working only with edge |
1743 | set_irq_type(irq, IRQT_RISING); | 1757 | * triggers (e.g. most ARM CPUs). Initial driver stress testing |
1744 | #endif | 1758 | * was on a system with single edge triggering, so most sorts of |
1745 | retval = usb_add_hcd(hcd, irq, SA_INTERRUPT); | 1759 | * triggering arrangement should work. |
1760 | */ | ||
1761 | retval = usb_add_hcd(hcd, irq, SA_INTERRUPT | SA_SHIRQ); | ||
1746 | if (retval != 0) | 1762 | if (retval != 0) |
1747 | goto err6; | 1763 | goto err6; |
1748 | 1764 | ||
@@ -1752,14 +1768,12 @@ sl811h_probe(struct device *dev) | |||
1752 | err6: | 1768 | err6: |
1753 | usb_put_hcd(hcd); | 1769 | usb_put_hcd(hcd); |
1754 | err5: | 1770 | err5: |
1755 | iounmap(data_reg); | 1771 | if (!ioaddr) |
1772 | iounmap(data_reg); | ||
1756 | err4: | 1773 | err4: |
1757 | release_mem_region(data->start, 1); | 1774 | if (!ioaddr) |
1758 | err3: | 1775 | iounmap(addr_reg); |
1759 | iounmap(addr_reg); | ||
1760 | err2: | 1776 | err2: |
1761 | release_mem_region(addr->start, 1); | ||
1762 | err1: | ||
1763 | DBG("init error, %d\n", retval); | 1777 | DBG("init error, %d\n", retval); |
1764 | return retval; | 1778 | return retval; |
1765 | } | 1779 | } |
@@ -1767,7 +1781,7 @@ sl811h_probe(struct device *dev) | |||
1767 | #ifdef CONFIG_PM | 1781 | #ifdef CONFIG_PM |
1768 | 1782 | ||
1769 | /* for this device there's no useful distinction between the controller | 1783 | /* for this device there's no useful distinction between the controller |
1770 | * and its root hub, except that the root hub only gets direct PM calls | 1784 | * and its root hub, except that the root hub only gets direct PM calls |
1771 | * when CONFIG_USB_SUSPEND is enabled. | 1785 | * when CONFIG_USB_SUSPEND is enabled. |
1772 | */ | 1786 | */ |
1773 | 1787 | ||
@@ -1821,20 +1835,22 @@ sl811h_resume(struct device *dev, u32 phase) | |||
1821 | #endif | 1835 | #endif |
1822 | 1836 | ||
1823 | 1837 | ||
1824 | static struct device_driver sl811h_driver = { | 1838 | /* this driver is exported so sl811_cs can depend on it */ |
1839 | struct device_driver sl811h_driver = { | ||
1825 | .name = (char *) hcd_name, | 1840 | .name = (char *) hcd_name, |
1826 | .bus = &platform_bus_type, | 1841 | .bus = &platform_bus_type, |
1827 | 1842 | ||
1828 | .probe = sl811h_probe, | 1843 | .probe = sl811h_probe, |
1829 | .remove = sl811h_remove, | 1844 | .remove = __devexit_p(sl811h_remove), |
1830 | 1845 | ||
1831 | .suspend = sl811h_suspend, | 1846 | .suspend = sl811h_suspend, |
1832 | .resume = sl811h_resume, | 1847 | .resume = sl811h_resume, |
1833 | }; | 1848 | }; |
1849 | EXPORT_SYMBOL(sl811h_driver); | ||
1834 | 1850 | ||
1835 | /*-------------------------------------------------------------------------*/ | 1851 | /*-------------------------------------------------------------------------*/ |
1836 | 1852 | ||
1837 | static int __init sl811h_init(void) | 1853 | static int __init sl811h_init(void) |
1838 | { | 1854 | { |
1839 | if (usb_disabled()) | 1855 | if (usb_disabled()) |
1840 | return -ENODEV; | 1856 | return -ENODEV; |
@@ -1844,8 +1860,8 @@ static int __init sl811h_init(void) | |||
1844 | } | 1860 | } |
1845 | module_init(sl811h_init); | 1861 | module_init(sl811h_init); |
1846 | 1862 | ||
1847 | static void __exit sl811h_cleanup(void) | 1863 | static void __exit sl811h_cleanup(void) |
1848 | { | 1864 | { |
1849 | driver_unregister(&sl811h_driver); | 1865 | driver_unregister(&sl811h_driver); |
1850 | } | 1866 | } |
1851 | module_exit(sl811h_cleanup); | 1867 | module_exit(sl811h_cleanup); |